From: David ‘Bombe’ Roden Date: Sun, 26 Aug 2012 16:57:20 +0000 (+0200) Subject: Mavenize jSite. X-Git-Tag: 0.11^2~19^2~9 X-Git-Url: https://git.pterodactylus.net/?p=jSite.git;a=commitdiff_plain;h=0e88169c3e8decfcd99f39f5ecf3a85df50c3fca Mavenize jSite. --- diff --git a/.classpath b/.classpath index 98130cb..683f2af 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,21 @@ - - - - + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project index c84cfc3..05eafae 100644 --- a/.project +++ b/.project @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/images/flag-de.png b/images/flag-de.png deleted file mode 100644 index d1b572b..0000000 Binary files a/images/flag-de.png and /dev/null differ diff --git a/images/flag-en.png b/images/flag-en.png deleted file mode 100644 index 33472a7..0000000 Binary files a/images/flag-en.png and /dev/null differ diff --git a/images/flag-fr.png b/images/flag-fr.png deleted file mode 100644 index 5793e70..0000000 Binary files a/images/flag-fr.png and /dev/null differ diff --git a/images/jsite-icon.png b/images/jsite-icon.png deleted file mode 100644 index fcddf6c..0000000 Binary files a/images/jsite-icon.png and /dev/null differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..732101c --- /dev/null +++ b/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + net.pterodactylus + jSite + 0.11-SNAPSHOT + + + pterodactylus + http://maven.pterodactylus.net/ + + + + UTF-8 + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + org.apache.maven.plugins + maven-jar-plugin + + + + de.todesbaum.jsite.main.Main + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.2-beta-5 + + + jar-with-dependencies + + + skip + + + + de.todesbaum.jsite.main.Main + + + + + + make-assembly + package + + single + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + http://download.oracle.com/javase/6/docs/api/ + http://java.pterodactylus.net/utils/apidocs/ + http://java.pterodactylus.net/utils.json/apidocs/ + + + + + + diff --git a/src/de/todesbaum/jsite/application/AbortedException.java b/src/de/todesbaum/jsite/application/AbortedException.java deleted file mode 100644 index 0dfd8c7..0000000 --- a/src/de/todesbaum/jsite/application/AbortedException.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * jSite - AbortedException.java - Copyright © 2010–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -/** - * Marker exception that signals that the user aborted an insert. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class AbortedException extends Exception { - - /* nothing here. */ - -} diff --git a/src/de/todesbaum/jsite/application/FileOption.java b/src/de/todesbaum/jsite/application/FileOption.java deleted file mode 100644 index c8824de..0000000 --- a/src/de/todesbaum/jsite/application/FileOption.java +++ /dev/null @@ -1,364 +0,0 @@ -/* - * jSite - FileOption.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -/** - * Container for various file options. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class FileOption { - - /** The default for the insert state. */ - private static final boolean DEFAULT_INSERT = true; - - /** The default for the insert redirect state. */ - private static final boolean DEFAULT_INSERT_REDIRECT = true; - - /** The default for the custom key. */ - private static final String DEFAULT_CUSTOM_KEY = "CHK@"; - - /** The default changed name. */ - private static final String DEFAULT_CHANGED_NAME = null; - - /** The insert state. */ - private boolean insert; - - /** Whether to force an insert. */ - private boolean forceInsert; - - /** Whether to insert a redirect. */ - private boolean insertRedirect; - - /** The hash of the last insert. */ - private String lastInsertHash; - - /** The edition of the last insert. */ - private int lastInsertEdition; - - /** The filename of the last insert. */ - private String lastInsertFilename; - - /** The current hash of the file. */ - private String currentHash; - - /** The custom key. */ - private String customKey; - - /** The changed name. */ - private String changedName; - - /** The default MIME type. */ - private final String defaultMimeType; - - /** The current MIME type. */ - private String mimeType; - - /** - * Creates new file options. - * - * @param defaultMimeType - * The default MIME type of the file - */ - public FileOption(String defaultMimeType) { - insert = DEFAULT_INSERT; - insertRedirect = DEFAULT_INSERT_REDIRECT; - customKey = DEFAULT_CUSTOM_KEY; - changedName = DEFAULT_CHANGED_NAME; - this.defaultMimeType = defaultMimeType; - mimeType = defaultMimeType; - } - - /** - * Returns the custom key. The custom key is only used when - * {@link #isInsert()} and {@link #isInsertRedirect()} both return {@code - * true}. - * - * @return The custom key - */ - public String getCustomKey() { - return customKey; - } - - /** - * Sets the custom key. The custom key is only used when {@link #isInsert()} - * and {@link #isInsertRedirect()} both return {@code true}. - * - * @param customKey - * The custom key - */ - public void setCustomKey(String customKey) { - if (customKey == null) { - this.customKey = ""; - } else { - this.customKey = customKey; - } - } - - /** - * Returns whether the file should be inserted. If a file is not inserted - * and {@link #isInsertRedirect()} is also {@code false}, the file will not - * be inserted at all. - * - * @see #setCustomKey(String) - * @return true if the file should be inserted, - * false otherwise - */ - public boolean isInsert() { - return insert; - } - - /** - * Sets whether the file should be inserted. If a file is not inserted and - * {@link #isInsertRedirect()} is also {@code false}, the file will not be - * inserted at all. - * - * @param insert - * true if the file should be inserted, - * false otherwise - */ - public void setInsert(boolean insert) { - this.insert = insert; - } - - /** - * Returns whether the insert of this file should be forced, even if its - * current hash matches the last insert hash. - * - * @return {@code true} to force the insert of this file, {@code false} - * otherwise - */ - public boolean isForceInsert() { - return forceInsert; - } - - /** - * Sets whether to force the insert of this file, even if its current hash - * matches the last insert hash. - * - * @param forceInsert - * {@code true} to force the insert of this file, {@code false} - * otherwise - * @return These file options - */ - public FileOption setForceInsert(boolean forceInsert) { - this.forceInsert = forceInsert; - return this; - } - - /** - * Returns whether a redirect to a different key should be inserted. This - * will only matter if {@link #isInsert()} returns {@code false}. The key - * that should be redirected to still needs to be specified via - * {@link #setCustomKey(String)}. - * - * @return {@code true} if a redirect should be inserted, {@code false} - * otherwise - */ - public boolean isInsertRedirect() { - return insertRedirect; - } - - /** - * Sets whether a redirect should be inserted. This will only matter if - * {@link #isInsert()} returns {@code false}, i.e. it has been - * {@link #setInsert(boolean)} to {@code false}. The key that should be - * redirected to still needs to be specified via - * {@link #setCustomKey(String)}. - * - * @param insertRedirect - * {@code true} if a redirect should be inserted, {@code false} - * otherwise - */ - public void setInsertRedirect(boolean insertRedirect) { - this.insertRedirect = insertRedirect; - } - - /** - * Returns the hash of the file when it was last inserted - * - * @return The last hash of the file - */ - public String getLastInsertHash() { - return lastInsertHash; - } - - /** - * Sets the hash of the file when it was last inserted. - * - * @param lastInsertHash - * The last hash of the file - * @return These file options - */ - public FileOption setLastInsertHash(String lastInsertHash) { - this.lastInsertHash = lastInsertHash; - return this; - } - - /** - * Returns the last edition at which this file was inserted. - * - * @return The last insert edition of this file - */ - public int getLastInsertEdition() { - return lastInsertEdition; - } - - /** - * Sets the last insert edition of this file. - * - * @param lastInsertEdition - * The last insert edition of this file - * @return These file options - */ - public FileOption setLastInsertEdition(int lastInsertEdition) { - this.lastInsertEdition = lastInsertEdition; - return this; - } - - /** - * Returns the name of the file when it was last inserted. - * - * @return The name of the file at the last insert - */ - public String getLastInsertFilename() { - return lastInsertFilename; - } - - /** - * Sets the name of the file when it was last inserted. - * - * @param lastInsertFilename - * The name of the file at the last insert. - * @return These file options - */ - public FileOption setLastInsertFilename(String lastInsertFilename) { - this.lastInsertFilename = lastInsertFilename; - return this; - } - - /** - * Returns the current hash of the file. This value is ony a temporary value - * that is copied to {@link #getLastInsertHash()} when a project has - * finished inserting. - * - * @see Project#onSuccessfulInsert() - * @return The current hash of the file - */ - public String getCurrentHash() { - return currentHash; - } - - /** - * Sets the current hash of the file. - * - * @param currentHash - * The current hash of the file - * @return These file options - */ - public FileOption setCurrentHash(String currentHash) { - this.currentHash = currentHash; - return this; - } - - /** - * Returns whether this file has a changed name. Use - * {@link #getChangedName()} is this method returns {@code true}. - * - * @return {@code true} if this file has a changed name, {@code false} - * otherwise - */ - public boolean hasChangedName() { - return (changedName != null) && (changedName.length() > 0); - } - - /** - * Returns the changed name for this file. This method will return {@code - * null} or an empty {@link String} if this file should not be renamed. - * - * @return The changed name, or {@code null} if this file should not be - * renamed - */ - public String getChangedName() { - return changedName; - } - - /** - * Sets the changed name for this file. Setting the changed file to {@code - * null} or an empty {@link String} will disable renaming. - * - * @param changedName - * The new changed name for this file - */ - public void setChangedName(String changedName) { - this.changedName = changedName; - } - - /** - * Sets the MIME type of the file. Setting the MIME type to - * null will set the MIME type to the default MIME type. - * - * @param mimeType - * The MIME type of the file - */ - public void setMimeType(String mimeType) { - if (mimeType == null) { - this.mimeType = defaultMimeType; - } else { - this.mimeType = mimeType; - } - } - - /** - * Returns the MIME type of the file. If no custom MIME type has been set, - * the default MIME type is returned. - * - * @return The MIME type of the file - */ - public String getMimeType() { - return mimeType; - } - - /** - * Returns whether the options for this file have been modified, i.e. are - * not at their default values. - * - * @return true if the options have been modified, - * false if they are at default values - */ - public boolean isCustom() { - if (insert != DEFAULT_INSERT) { - return true; - } - if (!customKey.equals(DEFAULT_CUSTOM_KEY)) { - return true; - } - if (((changedName != null) && !changedName.equals(DEFAULT_CHANGED_NAME)) || ((DEFAULT_CHANGED_NAME != null) && !DEFAULT_CHANGED_NAME.equals(changedName))) { - return true; - } - if (!defaultMimeType.equals(mimeType)) { - return true; - } - if (insertRedirect != DEFAULT_INSERT_REDIRECT) { - return true; - } - return false; - } - -} diff --git a/src/de/todesbaum/jsite/application/Freenet7Interface.java b/src/de/todesbaum/jsite/application/Freenet7Interface.java deleted file mode 100644 index 78250b2..0000000 --- a/src/de/todesbaum/jsite/application/Freenet7Interface.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * jSite - Freenet7Interface.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -import java.io.IOException; - -import de.todesbaum.util.freenet.fcp2.Client; -import de.todesbaum.util.freenet.fcp2.Connection; -import de.todesbaum.util.freenet.fcp2.GenerateSSK; -import de.todesbaum.util.freenet.fcp2.Message; -import de.todesbaum.util.freenet.fcp2.Node; - -/** - * Interface for freenet-related operations. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class Freenet7Interface { - - /** Random number to differentiate several jSites. */ - private static final int number = (int) (Math.random() * Integer.MAX_VALUE); - - /** Counter. */ - private static int counter = 0; - - /** The node to connect to. */ - private Node node; - - /** The connection to the node. */ - private Connection connection; - - /** - * Sets the hostname of the node. The default port for FCP2 connections ({@link Node#DEFAULT_PORT}) - * is used. - * - * @param hostname - * The hostname of the node - */ - public void setNodeAddress(String hostname) { - node = new Node(hostname); - connection = new Connection(node, "jSite-" + number + "-connection-" + counter++); - } - - /** - * Sets the hostname and the port of the node. - * - * @param hostname - * The hostname of the node - * @param port - * The port number of the node - */ - public void setNodeAddress(String hostname, int port) { - node = new Node(hostname, port); - connection = new Connection(node, "jSite-" + number + "-connection-" + counter++); - } - - /** - * Sets hostname and port from the given node. - * - * @param node - * The node to get the hostname and port from - */ - public void setNode(de.todesbaum.jsite.application.Node node) { - if (node != null) { - this.node = new Node(node.getHostname(), node.getPort()); - connection = new Connection(node, "jSite-" + number + "-connection-" + counter++); - } else { - this.node = null; - connection = null; - } - } - - /** - * Removes the current node from the interface. - */ - public void removeNode() { - node = null; - connection = null; - } - - /** - * Returns the node this interface is connecting to. - * - * @return The node - */ - public Node getNode() { - return node; - } - - /** - * Creates a new connection to the current node with the given identifier. - * - * @param identifier - * The identifier of the connection - * @return The connection to the node - */ - public Connection getConnection(String identifier) { - return new Connection(node, identifier); - } - - /** - * Checks whether the current node is connected. If the node is not - * connected, a connection will be tried. - * - * @return true if the node is connected, false - * otherwise - * @throws IOException - * if an I/O error occurs communicating with the node - */ - public boolean isNodePresent() throws IOException { - if (!connection.isConnected()) { - return connection.connect(); - } - return true; - } - - /** - * Generates an SSK key pair. - * - * @return An array of strings, the first one being the generated private - * (insert) URI and the second one being the generated public - * (request) URI - * @throws IOException - * if an I/O error occurs communicating with the node - */ - public String[] generateKeyPair() throws IOException { - if (!isNodePresent()) { - throw new IOException("Node is offline."); - } - GenerateSSK generateSSK = new GenerateSSK(); - Client client = new Client(connection, generateSSK); - Message keypairMessage = client.readMessage(); - return new String[] { keypairMessage.get("InsertURI"), keypairMessage.get("RequestURI") }; - } - - /** - * Checks whether the interface has already been configured with a node. - * - * @return true if this interface already has a node set, - * false otherwise - */ - public boolean hasNode() { - return (node != null) && (connection != null); - } - -} diff --git a/src/de/todesbaum/jsite/application/InsertListener.java b/src/de/todesbaum/jsite/application/InsertListener.java deleted file mode 100644 index 9b6b332..0000000 --- a/src/de/todesbaum/jsite/application/InsertListener.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * jSite - InsertListener.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -import java.util.EventListener; - -/** - * Interface for objects that want to be notified abount insert events. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public interface InsertListener extends EventListener { - - /** - * Enumeration for the different error situations. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ - public static enum ErrorType { - - /** The key does already exist. */ - KEY_COLLISION, - - /** The route to the key was not found. */ - ROUTE_NOT_FOUND, - - /** The data was not found. */ - DATA_NOT_FOUND, - - /** Error in the FCP communication. */ - FCP_ERROR, - - /** General error in the communication. */ - IO_ERROR - } - - /** - * Notifies a listener that an insert has started. - * - * @param project - * The project that is now being inserted - */ - public void projectInsertStarted(Project project); - - /** - * Notifies a listener that the upload of a project has finished and the - * inserting will start now. - * - * @param project - * The project that has been uploaded - */ - public void projectUploadFinished(Project project); - - /** - * Notifies a listener that a project insert has generated a URI. - * - * @param project - * The project being inserted - * @param uri - * The generated URI - */ - public void projectURIGenerated(Project project, String uri); - - /** - * Notifies a listener that an insert has made some progress. - * - * @param project - * The project being inserted - * @param succeeded - * The number of succeeded blocks - * @param failed - * The number of failed blocks - * @param fatal - * The number of fatally failed blocks - * @param total - * The total number of blocks - * @param finalized - * true if the total number of blocks has been - * finalized, false otherwise - */ - public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized); - - /** - * Notifies a listener that a project insert has finished. - * - * @param project - * The project being inserted - * @param success - * true if the insert succeeded, false - * otherwise - * @param cause - * The cause of a failure, if any (may be null) - */ - public void projectInsertFinished(Project project, boolean success, Throwable cause); - -} diff --git a/src/de/todesbaum/jsite/application/KeyDialog.java b/src/de/todesbaum/jsite/application/KeyDialog.java deleted file mode 100644 index 4b64f72..0000000 --- a/src/de/todesbaum/jsite/application/KeyDialog.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * jSite - KeyDialog.java - Copyright © 2010–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.KeyEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.io.IOException; -import java.text.MessageFormat; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSeparator; -import javax.swing.JTextField; -import javax.swing.KeyStroke; -import javax.swing.SwingConstants; - -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.jsite.i18n.I18nContainer; - -/** - * A dialog that lets the user edit the private and public key for a project. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class KeyDialog extends JDialog { - - /** Interface to the freenet node. */ - private final Freenet7Interface freenetInterface; - - /** The public key. */ - private String publicKey; - - /** The private key. */ - private String privateKey; - - /** The “OK” button’s action. */ - private Action okAction; - - /** The “Cancel” button’s action. */ - private Action cancelAction; - - /** The “Regenerate” button’s action. */ - private Action generateAction; - - /** The text field for the private key. */ - private JTextField privateKeyTextField; - - /** The text field for the public key. */ - private JTextField publicKeyTextField; - - /** Whether the dialog was cancelled. */ - private boolean cancelled; - - /** - * Creates a new key dialog. - * - * @param freenetInterface - * Interface to the freenet node - * @param parent - * The parent frame - */ - public KeyDialog(Freenet7Interface freenetInterface, JFrame parent) { - super(parent, I18n.getMessage("jsite.key-dialog.title"), true); - this.freenetInterface = freenetInterface; - addWindowListener(new WindowAdapter() { - - @Override - @SuppressWarnings("synthetic-access") - public void windowClosing(WindowEvent windowEvent) { - actionCancel(); - } - }); - initDialog(); - } - - // - // ACCESSORS - // - - /** - * Returns whether the dialog was cancelled. - * - * @return {@code true} if the dialog was cancelled, {@code false} otherwise - */ - public boolean wasCancelled() { - return cancelled; - } - - /** - * Returns the public key. - * - * @return The public key - */ - public String getPublicKey() { - return publicKey; - } - - /** - * Sets the public key. - * - * @param publicKey - * The public key - */ - public void setPublicKey(String publicKey) { - this.publicKey = publicKey; - publicKeyTextField.setText(publicKey); - pack(); - } - - /** - * Returns the private key. - * - * @return The private key - */ - public String getPrivateKey() { - return privateKey; - } - - /** - * Sets the private key. - * - * @param privateKey - * The private key - */ - public void setPrivateKey(String privateKey) { - this.privateKey = privateKey; - privateKeyTextField.setText(privateKey); - pack(); - } - - // - // ACTIONS - // - - /** - * {@inheritDoc} - */ - @Override - public void pack() { - super.pack(); - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - setLocation((screenSize.width - getWidth()) / 2, (screenSize.height - getHeight()) / 2); - } - - // - // PRIVATE METHODS - // - - /** - * Creates all necessary actions. - */ - private void createActions() { - okAction = new AbstractAction(I18n.getMessage("jsite.general.ok")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionOk(); - } - }; - okAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.ok.tooltip")); - okAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ENTER); - - cancelAction = new AbstractAction(I18n.getMessage("jsite.general.cancel")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionCancel(); - } - }; - cancelAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.cancel.tooltip")); - cancelAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ESCAPE); - - generateAction = new AbstractAction(I18n.getMessage("jsite.key-dialog.button.generate")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionGenerate(); - } - }; - generateAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.generate.tooltip")); - generateAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK)); - } - - /** - * Initializes the dialog and all its components. - */ - private void initDialog() { - createActions(); - JPanel dialogPanel = new JPanel(new BorderLayout(12, 12)); - dialogPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); - - JPanel contentPanel = new JPanel(new GridBagLayout()); - dialogPanel.add(contentPanel, BorderLayout.CENTER); - - final JLabel keysLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.keys")); - contentPanel.add(keysLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - - final JLabel privateKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.private-key")); - contentPanel.add(privateKeyLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0)); - - privateKeyTextField = new JTextField(); - contentPanel.add(privateKeyTextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0)); - - final JLabel publicKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.public-key")); - contentPanel.add(publicKeyLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - - publicKeyTextField = new JTextField(); - contentPanel.add(publicKeyTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0)); - - final JLabel actionsLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.actions")); - contentPanel.add(actionsLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0)); - - JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12)); - actionButtonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12)); - contentPanel.add(actionButtonPanel, new GridBagConstraints(0, 4, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0)); - - actionButtonPanel.add(new JButton(generateAction)); - - JPanel separatorPanel = new JPanel(new BorderLayout(12, 12)); - dialogPanel.add(separatorPanel, BorderLayout.PAGE_END); - separatorPanel.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.PAGE_START); - - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12)); - buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12)); - separatorPanel.add(buttonPanel, BorderLayout.CENTER); - buttonPanel.add(new JButton(okAction)); - buttonPanel.add(new JButton(cancelAction)); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - keysLabel.setText(I18n.getMessage("jsite.key-dialog.label.keys")); - privateKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.private-key")); - publicKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.public-key")); - actionsLabel.setText(I18n.getMessage("jsite.key-dialog.label.actions")); - } - }); - - getContentPane().add(dialogPanel, BorderLayout.CENTER); - pack(); - setResizable(false); - } - - // - // PRIVATE ACTIONS - // - - /** - * Quits the dialog, accepting all changes. - */ - private void actionOk() { - publicKey = publicKeyTextField.getText(); - privateKey = privateKeyTextField.getText(); - cancelled = false; - setVisible(false); - } - - /** - * Quits the dialog, discarding all changes. - */ - private void actionCancel() { - cancelled = true; - setVisible(false); - } - - /** - * Generates a new key pair. - */ - private void actionGenerate() { - if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.generate-new-key"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - return; - } - String[] keyPair = null; - try { - keyPair = freenetInterface.generateKeyPair(); - } catch (IOException ioe1) { - JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.keygen.io-error"), ioe1.getMessage()), null, JOptionPane.ERROR_MESSAGE); - return; - } - publicKeyTextField.setText(keyPair[1].substring(keyPair[1].indexOf('@') + 1, keyPair[1].lastIndexOf('/'))); - privateKeyTextField.setText(keyPair[0].substring(keyPair[0].indexOf('@') + 1, keyPair[0].lastIndexOf('/'))); - pack(); - } - -} diff --git a/src/de/todesbaum/jsite/application/Node.java b/src/de/todesbaum/jsite/application/Node.java deleted file mode 100644 index df7492f..0000000 --- a/src/de/todesbaum/jsite/application/Node.java +++ /dev/null @@ -1,166 +0,0 @@ -/* - * jSite - Node.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -/** - * Container for node information. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class Node extends de.todesbaum.util.freenet.fcp2.Node { - - /** The name of the node. */ - protected String name; - - /** - * Creates a new node with the given hostname and the default port. - * - * @see de.todesbaum.util.freenet.fcp2.Node#DEFAULT_PORT - * @param hostname - * The hostname of the new node - */ - public Node(String hostname) { - this(hostname, DEFAULT_PORT); - } - - /** - * Creates a new node with the given hostname and port. - * - * @param hostname - * The hostname of the new node - * @param port - * The port of the new node - */ - public Node(String hostname, int port) { - this(hostname, port, ""); - } - - /** - * Creates a new node with the given hostname, port, and name. - * - * @param hostname - * The hostname of the new node - * @param port - * The port of the new node - * @param name - * The name of the node - */ - public Node(String hostname, int port, String name) { - super(hostname, port); - this.name = name; - } - - /** - * Creates a new node that gets its settings from the given node. - * - * @param node - * The node to copy - */ - public Node(Node node) { - this(node.getHostname(), node.getPort()); - } - - /** - * Creates a new node from the given node, overwriting the name. - * - * @param node - * The node to copy from - * @param name - * The new name of the node - */ - public Node(Node node, String name) { - this(node.getHostname(), node.getPort(), name); - } - - /** - * Sets the name of the node. - * - * @param name - * The name of the node - */ - public void setName(String name) { - this.name = name; - } - - /** - * Returns the name of the node. - * - * @return The name of the node - */ - public String getName() { - return name; - } - - /** - * Sets the hostname of the node. - * - * @param hostname - * The hostname of the node - */ - public void setHostname(String hostname) { - this.hostname = hostname; - } - - /** - * Sets the port of the node. - * - * @param port - * The port of the node - */ - public void setPort(int port) { - this.port = port; - } - - /** - * {@inheritDoc} - *

- * A node is considered as being equal to this node its name, hostname, and - * port equal their counterparts in this node. - */ - @Override - public boolean equals(Object o) { - if ((o == null) || !(o instanceof Node)) { - return false; - } - Node node = (Node) o; - return name.equals(node.name) && hostname.equals(node.hostname) && (port == node.port); - } - - /** - * {@inheritDoc} - *

- * The hashcode for a node is created from its name, its hostname, and its - * port. - */ - @Override - public int hashCode() { - return name.hashCode() ^ hostname.hashCode() ^ port; - } - - /** - * {@inheritDoc} - *

- * Creates a textual representation of this node. - */ - @Override - public String toString() { - return name + " (" + hostname + ":" + port + ")"; - } - -} diff --git a/src/de/todesbaum/jsite/application/Project.java b/src/de/todesbaum/jsite/application/Project.java deleted file mode 100644 index fa0b774..0000000 --- a/src/de/todesbaum/jsite/application/Project.java +++ /dev/null @@ -1,441 +0,0 @@ -/* - * jSite - Project.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -import java.io.File; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import de.todesbaum.util.mime.DefaultMIMETypes; - -/** - * Container for project information. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class Project implements Comparable { - - /** The name of the project. */ - protected String name; - - /** The description of the project. */ - protected String description; - - /** The insert URI of the project. */ - protected String insertURI; - - /** The request URI of the project. */ - protected String requestURI; - - /** The index file of the project. */ - protected String indexFile; - - /** The local path of the project. */ - protected String localPath; - - /** The remote path of the URI. */ - protected String path; - - /** The time of the last insertion. */ - protected long lastInsertionTime; - - /** The edition to insert to. */ - protected int edition; - - /** Whether to ignore hidden directory. */ - private boolean ignoreHiddenFiles; - - /** Options for files. */ - protected Map fileOptions = new HashMap(); - - /** - * Empty constructor. - */ - public Project() { - /* do nothing. */ - } - - /** - * Creates a new project from an existing one. - * - * @param project - * The project to clone - */ - public Project(Project project) { - name = project.name; - description = project.description; - insertURI = project.insertURI; - requestURI = project.requestURI; - path = project.path; - edition = project.edition; - localPath = project.localPath; - indexFile = project.indexFile; - lastInsertionTime = project.lastInsertionTime; - ignoreHiddenFiles = project.ignoreHiddenFiles; - fileOptions = new HashMap(project.fileOptions); - } - - /** - * Returns the name of the project. - * - * @return The name of the project - */ - public String getName() { - return name; - } - - /** - * Sets the name of the project. - * - * @param name - * The name of the project - */ - public void setName(String name) { - this.name = name; - } - - /** - * Returns the description of the project. - * - * @return The description of the project - */ - public String getDescription() { - return description; - } - - /** - * Sets the description of the project. - * - * @param description - * The description of the project - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * Returns the local path of the project. - * - * @return The local path of the project - */ - public String getLocalPath() { - return localPath; - } - - /** - * Sets the local path of the project. - * - * @param localPath - * The local path of the project - */ - public void setLocalPath(String localPath) { - this.localPath = localPath; - } - - /** - * Returns the name of the index file of the project, relative to the - * project’s local path. - * - * @return The name of the index file of the project - */ - public String getIndexFile() { - return indexFile; - } - - /** - * Sets the name of the index file of the project, relative to the project’s - * local path. - * - * @param indexFile - * The name of the index file of the project - */ - public void setIndexFile(String indexFile) { - this.indexFile = indexFile; - } - - /** - * Returns the time the project was last inserted, in milliseconds since the - * epoch. - * - * @return The time of the last insertion - */ - public long getLastInsertionTime() { - return lastInsertionTime; - } - - /** - * Sets the time the project was last inserted, in milliseconds since the - * last epoch. - * - * @param lastInserted - * The time of the last insertion - */ - public void setLastInsertionTime(long lastInserted) { - lastInsertionTime = lastInserted; - } - - /** - * Returns the remote path of the project. The remote path is the path that - * directly follows the request URI of the project. - * - * @return The remote path of the project - */ - public String getPath() { - return path; - } - - /** - * Sets the remote path of the project. The remote path is the path that - * directly follows the request URI of the project. - * - * @param path - * The remote path of the project - */ - public void setPath(String path) { - this.path = path; - } - - /** - * Returns the insert URI of the project. - * - * @return The insert URI of the project - */ - public String getInsertURI() { - return insertURI; - } - - /** - * Sets the insert URI of the project. - * - * @param insertURI - * The insert URI of the project - */ - public void setInsertURI(String insertURI) { - this.insertURI = shortenURI(insertURI); - } - - /** - * Returns the request URI of the project. - * - * @return The request URI of the project - */ - public String getRequestURI() { - return requestURI; - } - - /** - * Sets the request URI of the project. - * - * @param requestURI - * The request URI of the project - */ - public void setRequestURI(String requestURI) { - this.requestURI = shortenURI(requestURI); - } - - /** - * Returns whether hidden files are ignored, i.e. not inserted. - * - * @return {@code true} if hidden files are not inserted, {@code false} - * otherwise - */ - public boolean isIgnoreHiddenFiles() { - return ignoreHiddenFiles; - } - - /** - * Sets whether hidden files are ignored, i.e. not inserted. - * - * @param ignoreHiddenFiles - * {@code true} if hidden files are not inserted, {@code false} - * otherwise - */ - public void setIgnoreHiddenFiles(boolean ignoreHiddenFiles) { - this.ignoreHiddenFiles = ignoreHiddenFiles; - } - - /** - * {@inheritDoc} - *

- * This method returns the name of the project. - */ - @Override - public String toString() { - return name; - } - - /** - * Shortens the given URI by removing scheme and key-type prefixes. - * - * @param uri - * The URI to shorten - * @return The shortened URI - */ - private String shortenURI(String uri) { - String shortUri = uri; - if (shortUri.startsWith("freenet:")) { - shortUri = shortUri.substring("freenet:".length()); - } - if (shortUri.startsWith("SSK@")) { - shortUri = shortUri.substring("SSK@".length()); - } - if (shortUri.startsWith("USK@")) { - shortUri = shortUri.substring("USK@".length()); - } - if (shortUri.endsWith("/")) { - shortUri = shortUri.substring(0, shortUri.length() - 1); - } - return shortUri; - } - - /** - * Shortens the name of the given file by removing the local path of the - * project and leading file separators. - * - * @param file - * The file whose name should be shortened - * @return The shortened name of the file - */ - public String shortenFilename(File file) { - String filename = file.getPath(); - if (filename.startsWith(localPath)) { - filename = filename.substring(localPath.length()); - if (filename.startsWith(File.separator)) { - filename = filename.substring(1); - } - } - return filename; - } - - /** - * Returns the options for the file with the given name. If the file does - * not yet have any options, a new set of default options is created and - * returned. - * - * @param filename - * The name of the file, relative to the project root - * @return The options for the file - */ - public FileOption getFileOption(String filename) { - FileOption fileOption = fileOptions.get(filename); - if (fileOption == null) { - fileOption = new FileOption(DefaultMIMETypes.guessMIMEType(filename)); - fileOptions.put(filename, fileOption); - } - return fileOption; - } - - /** - * Sets options for a file. - * - * @param filename - * The filename to set the options for, relative to the project - * root - * @param fileOption - * The options to set for the file, or null to - * remove the options for the file - */ - public void setFileOption(String filename, FileOption fileOption) { - if (fileOption != null) { - fileOptions.put(filename, fileOption); - } else { - fileOptions.remove(filename); - } - } - - /** - * Returns all file options. - * - * @return All file options - */ - public Map getFileOptions() { - return Collections.unmodifiableMap(fileOptions); - } - - /** - * Sets all file options. - * - * @param fileOptions - * The file options - */ - public void setFileOptions(Map fileOptions) { - this.fileOptions.clear(); - this.fileOptions.putAll(fileOptions); - } - - /** - * {@inheritDoc} - *

- * Projects are compared by their name only. - */ - public int compareTo(Project project) { - return name.compareToIgnoreCase(project.name); - } - - /** - * Returns the edition of the project. - * - * @return The edition of the project - */ - public int getEdition() { - return edition; - } - - /** - * Sets the edition of the project. - * - * @param edition - * The edition to set - */ - public void setEdition(int edition) { - this.edition = edition; - } - - /** - * Constructs the final request URI including the edition number. - * - * @param offset - * The offset for the edition number - * @return The final request URI - */ - public String getFinalRequestURI(int offset) { - return "USK@" + requestURI + "/" + path + "/" + (edition + offset) + "/"; - } - - /** - * Performs some post-processing on the project after it was inserted - * successfully. At the moment it copies the current hashes of all file - * options to the last insert hashes, updating the hashes for the next - * insert. - */ - public void onSuccessfulInsert() { - for (Entry fileOptionEntry : fileOptions.entrySet()) { - FileOption fileOption = fileOptionEntry.getValue(); - if ((fileOption.getCurrentHash() != null) && (fileOption.getCurrentHash().length() > 0) && (!fileOption.getCurrentHash().equals(fileOption.getLastInsertHash()) || fileOption.isForceInsert())) { - fileOption.setLastInsertEdition(edition); - fileOption.setLastInsertHash(fileOption.getCurrentHash()); - fileOption.setLastInsertFilename(fileOption.hasChangedName() ? fileOption.getChangedName() : fileOptionEntry.getKey()); - } - fileOption.setForceInsert(false); - } - } - -} diff --git a/src/de/todesbaum/jsite/application/ProjectInserter.java b/src/de/todesbaum/jsite/application/ProjectInserter.java deleted file mode 100644 index b5f2e16..0000000 --- a/src/de/todesbaum/jsite/application/ProjectInserter.java +++ /dev/null @@ -1,694 +0,0 @@ -/* - * jSite - ProjectInserter.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.CountDownLatch; -import java.util.logging.Level; -import java.util.logging.Logger; - -import de.todesbaum.jsite.gui.FileScanner; -import de.todesbaum.jsite.gui.FileScanner.ScannedFile; -import de.todesbaum.jsite.gui.FileScannerListener; -import de.todesbaum.util.freenet.fcp2.Client; -import de.todesbaum.util.freenet.fcp2.ClientPutComplexDir; -import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; -import de.todesbaum.util.freenet.fcp2.Connection; -import de.todesbaum.util.freenet.fcp2.DirectFileEntry; -import de.todesbaum.util.freenet.fcp2.FileEntry; -import de.todesbaum.util.freenet.fcp2.Message; -import de.todesbaum.util.freenet.fcp2.PriorityClass; -import de.todesbaum.util.freenet.fcp2.RedirectFileEntry; -import de.todesbaum.util.freenet.fcp2.Verbosity; -import de.todesbaum.util.io.StreamCopier.ProgressListener; - -/** - * Manages project inserts. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class ProjectInserter implements FileScannerListener, Runnable { - - /** The logger. */ - private static final Logger logger = Logger.getLogger(ProjectInserter.class.getName()); - - /** Random number for FCP instances. */ - private static final int random = (int) (Math.random() * Integer.MAX_VALUE); - - /** Counter for FCP connection identifier. */ - private static int counter = 0; - - /** The list of insert listeners. */ - private List insertListeners = new ArrayList(); - - /** The freenet interface. */ - protected Freenet7Interface freenetInterface; - - /** The project to insert. */ - protected Project project; - - /** The file scanner. */ - private FileScanner fileScanner; - - /** Object used for synchronization. */ - protected final Object lockObject = new Object(); - - /** The temp directory. */ - private String tempDirectory; - - /** The current connection. */ - private Connection connection; - - /** Whether the insert is cancelled. */ - private volatile boolean cancelled = false; - - /** Progress listener for payload transfers. */ - private ProgressListener progressListener; - - /** Whether to use “early encode.” */ - private boolean useEarlyEncode; - - /** The insert priority. */ - private PriorityClass priority; - - /** The manifest putter. */ - private ManifestPutter manifestPutter; - - /** - * Adds a listener to the list of registered listeners. - * - * @param insertListener - * The listener to add - */ - public void addInsertListener(InsertListener insertListener) { - insertListeners.add(insertListener); - } - - /** - * Removes a listener from the list of registered listeners. - * - * @param insertListener - * The listener to remove - */ - public void removeInsertListener(InsertListener insertListener) { - insertListeners.remove(insertListener); - } - - /** - * Notifies all listeners that the project insert has started. - * - * @see InsertListener#projectInsertStarted(Project) - */ - protected void fireProjectInsertStarted() { - for (InsertListener insertListener : insertListeners) { - insertListener.projectInsertStarted(project); - } - } - - /** - * Notifies all listeners that the insert has generated a URI. - * - * @see InsertListener#projectURIGenerated(Project, String) - * @param uri - * The generated URI - */ - protected void fireProjectURIGenerated(String uri) { - for (InsertListener insertListener : insertListeners) { - insertListener.projectURIGenerated(project, uri); - } - } - - /** - * Notifies all listeners that the insert has made some progress. - * - * @see InsertListener#projectUploadFinished(Project) - */ - protected void fireProjectUploadFinished() { - for (InsertListener insertListener : insertListeners) { - insertListener.projectUploadFinished(project); - } - } - - /** - * Notifies all listeners that the insert has made some progress. - * - * @see InsertListener#projectInsertProgress(Project, int, int, int, int, - * boolean) - * @param succeeded - * The number of succeeded blocks - * @param failed - * The number of failed blocks - * @param fatal - * The number of fatally failed blocks - * @param total - * The total number of blocks - * @param finalized - * true if the total number of blocks has already - * been finalized, false otherwise - */ - protected void fireProjectInsertProgress(int succeeded, int failed, int fatal, int total, boolean finalized) { - for (InsertListener insertListener : insertListeners) { - insertListener.projectInsertProgress(project, succeeded, failed, fatal, total, finalized); - } - } - - /** - * Notifies all listeners the project insert has finished. - * - * @see InsertListener#projectInsertFinished(Project, boolean, Throwable) - * @param success - * true if the project was inserted successfully, - * false if it failed - * @param cause - * The cause of the failure, if any - */ - protected void fireProjectInsertFinished(boolean success, Throwable cause) { - for (InsertListener insertListener : insertListeners) { - insertListener.projectInsertFinished(project, success, cause); - } - } - - /** - * Sets the project to insert. - * - * @param project - * The project to insert - */ - public void setProject(Project project) { - this.project = project; - } - - /** - * Sets the freenet interface to use. - * - * @param freenetInterface - * The freenet interface to use - */ - public void setFreenetInterface(Freenet7Interface freenetInterface) { - this.freenetInterface = freenetInterface; - } - - /** - * Sets the temp directory to use. - * - * @param tempDirectory - * The temp directory to use, or {@code null} to use the system - * default - */ - public void setTempDirectory(String tempDirectory) { - this.tempDirectory = tempDirectory; - } - - /** - * Sets whether to use the “early encode“ flag for the insert. - * - * @param useEarlyEncode - * {@code true} to set the “early encode” flag for the insert, - * {@code false} otherwise - */ - public void setUseEarlyEncode(boolean useEarlyEncode) { - this.useEarlyEncode = useEarlyEncode; - } - - /** - * Sets the insert priority. - * - * @param priority - * The insert priority - */ - public void setPriority(PriorityClass priority) { - this.priority = priority; - } - - /** - * Sets the manifest putter to use for inserts. - * - * @param manifestPutter - * The manifest putter to use - */ - public void setManifestPutter(ManifestPutter manifestPutter) { - this.manifestPutter = manifestPutter; - } - - /** - * Starts the insert. - * - * @param progressListener - * Listener to notify on progress events - */ - public void start(ProgressListener progressListener) { - cancelled = false; - this.progressListener = progressListener; - fileScanner = new FileScanner(project); - fileScanner.addFileScannerListener(this); - new Thread(fileScanner).start(); - } - - /** - * Stops the current insert. - */ - public void stop() { - cancelled = true; - synchronized (lockObject) { - if (connection != null) { - connection.disconnect(); - } - } - } - - /** - * Creates an input stream that delivers the given file, replacing edition - * tokens in the file’s content, if necessary. - * - * @param filename - * The name of the file - * @param fileOption - * The file options - * @param edition - * The current edition - * @param length - * An array containing a single long which is used to - * return the final length of the file, after all - * replacements - * @return The input stream for the file - * @throws IOException - * if an I/O error occurs - */ - private InputStream createFileInputStream(String filename, FileOption fileOption, int edition, long[] length) throws IOException { - File file = new File(project.getLocalPath(), filename); - length[0] = file.length(); - return new FileInputStream(file); - } - - /** - * Creates a file entry suitable for handing in to - * {@link ClientPutComplexDir#addFileEntry(FileEntry)}. - * - * @param file - * The name and hash of the file to insert - * @param edition - * The current edition - * @return A file entry for the given file - */ - private FileEntry createFileEntry(ScannedFile file, int edition) { - FileEntry fileEntry = null; - String filename = file.getFilename(); - FileOption fileOption = project.getFileOption(filename); - if (fileOption.isInsert()) { - fileOption.setCurrentHash(file.getHash()); - /* check if file was modified. */ - if (!fileOption.isForceInsert() && file.getHash().equals(fileOption.getLastInsertHash())) { - /* only insert a redirect. */ - logger.log(Level.FINE, String.format("Inserting redirect to edition %d for %s.", fileOption.getLastInsertEdition(), filename)); - return new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), "SSK@" + project.getRequestURI() + "/" + project.getPath() + "-" + fileOption.getLastInsertEdition() + "/" + fileOption.getLastInsertFilename()); - } - try { - long[] fileLength = new long[1]; - InputStream fileEntryInputStream = createFileInputStream(filename, fileOption, edition, fileLength); - fileEntry = new DirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileEntryInputStream, fileLength[0]); - } catch (IOException ioe1) { - /* ignore, null is returned. */ - } - } else { - if (fileOption.isInsertRedirect()) { - fileEntry = new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileOption.getCustomKey()); - } - } - return fileEntry; - } - - /** - * Validates the given project. The project will be checked for any invalid - * conditions, such as invalid insert or request keys, missing path names, - * missing default file, and so on. - * - * @param project - * The project to check - * @return The encountered warnings and errors - */ - public static CheckReport validateProject(Project project) { - CheckReport checkReport = new CheckReport(); - if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) { - checkReport.addIssue("error.no-local-path", true); - } - if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) { - checkReport.addIssue("error.no-path", true); - } - if ((project.getIndexFile() == null) || (project.getIndexFile().length() == 0)) { - checkReport.addIssue("warning.empty-index", false); - } else { - File indexFile = new File(project.getLocalPath(), project.getIndexFile()); - if (!indexFile.exists()) { - checkReport.addIssue("error.index-missing", true); - } - } - String indexFile = project.getIndexFile(); - boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0); - List 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 = fileOptionEntries.isEmpty(); - for (Entry fileOptionEntry : fileOptionEntries) { - String fileName = fileOptionEntry.getKey(); - FileOption fileOption = fileOptionEntry.getValue(); - insert |= fileOption.isInsert() || fileOption.isInsertRedirect(); - if (fileName.equals(project.getIndexFile()) && !fileOption.isInsert() && !fileOption.isInsertRedirect()) { - checkReport.addIssue("error.index-not-inserted", true); - } - if (!fileOption.isInsert() && fileOption.isInsertRedirect() && ((fileOption.getCustomKey().length() == 0) || "CHK@".equals(fileOption.getCustomKey()))) { - checkReport.addIssue("error.no-custom-key", true, fileName); - } - } - if (!insert) { - checkReport.addIssue("error.no-files-to-insert", true); - } - Set fileNames = new HashSet(); - for (Entry fileOptionEntry : fileOptionEntries) { - FileOption fileOption = fileOptionEntry.getValue(); - if (!fileOption.isInsert() && !fileOption.isInsertRedirect()) { - logger.log(Level.FINEST, "Ignoring {0}.", fileOptionEntry.getKey()); - continue; - } - String fileName = fileOptionEntry.getKey(); - if (fileOption.hasChangedName()) { - fileName = fileOption.getChangedName(); - } - logger.log(Level.FINEST, "Adding “{0}” for {1}.", new Object[] { fileName, fileOptionEntry.getKey() }); - if (!fileNames.add(fileName)) { - checkReport.addIssue("error.duplicate-file", true, fileName); - } - } - long totalSize = 0; - FileScanner fileScanner = new FileScanner(project); - final CountDownLatch completionLatch = new CountDownLatch(1); - fileScanner.addFileScannerListener(new FileScannerListener() { - - @Override - public void fileScannerFinished(FileScanner fileScanner) { - completionLatch.countDown(); - } - }); - new Thread(fileScanner).start(); - while (completionLatch.getCount() > 0) { - try { - completionLatch.await(); - } catch (InterruptedException ie1) { - /* TODO: logging */ - } - } - for (ScannedFile scannedFile : fileScanner.getFiles()) { - String fileName = scannedFile.getFilename(); - FileOption fileOption = project.getFileOption(fileName); - if ((fileOption != null) && !fileOption.isInsert()) { - continue; - } - totalSize += new File(project.getLocalPath(), fileName).length(); - } - if (totalSize > 2 * 1024 * 1024) { - checkReport.addIssue("warning.site-larger-than-2-mib", false); - } - return checkReport; - } - - /** - * {@inheritDoc} - */ - public void run() { - fireProjectInsertStarted(); - List files = fileScanner.getFiles(); - - /* create connection to node */ - synchronized (lockObject) { - connection = freenetInterface.getConnection("project-insert-" + random + counter++); - } - connection.setTempDirectory(tempDirectory); - boolean connected = false; - Throwable cause = null; - try { - connected = connection.connect(); - } catch (IOException e1) { - cause = e1; - } - - if (!connected || cancelled) { - fireProjectInsertFinished(false, cancelled ? new AbortedException() : cause); - return; - } - - Client client = new Client(connection); - - /* collect files */ - int edition = project.getEdition(); - String dirURI = "USK@" + project.getInsertURI() + "/" + project.getPath() + "/" + edition + "/"; - ClientPutComplexDir putDir = new ClientPutComplexDir("dir-" + counter++, dirURI, tempDirectory); - if ((project.getIndexFile() != null) && (project.getIndexFile().length() > 0)) { - putDir.setDefaultName(project.getIndexFile()); - } - putDir.setVerbosity(Verbosity.ALL); - putDir.setMaxRetries(-1); - putDir.setEarlyEncode(useEarlyEncode); - putDir.setPriorityClass(priority); - putDir.setManifestPutter(manifestPutter); - for (ScannedFile file : files) { - FileEntry fileEntry = createFileEntry(file, edition); - if (fileEntry != null) { - try { - putDir.addFileEntry(fileEntry); - } catch (IOException ioe1) { - fireProjectInsertFinished(false, ioe1); - return; - } - } - } - - /* start request */ - try { - client.execute(putDir, progressListener); - fireProjectUploadFinished(); - } catch (IOException ioe1) { - fireProjectInsertFinished(false, ioe1); - return; - } - - /* parse progress and success messages */ - String finalURI = null; - boolean success = false; - boolean finished = false; - boolean disconnected = false; - while (!finished && !cancelled) { - Message message = client.readMessage(); - finished = (message == null) || (disconnected = client.isDisconnected()); - logger.log(Level.FINE, "Received message: " + message); - if (!finished) { - @SuppressWarnings("null") - String messageName = message.getName(); - if ("URIGenerated".equals(messageName)) { - finalURI = message.get("URI"); - fireProjectURIGenerated(finalURI); - } - if ("SimpleProgress".equals(messageName)) { - int total = Integer.parseInt(message.get("Total")); - int succeeded = Integer.parseInt(message.get("Succeeded")); - int fatal = Integer.parseInt(message.get("FatallyFailed")); - int failed = Integer.parseInt(message.get("Failed")); - boolean finalized = Boolean.parseBoolean(message.get("FinalizedTotal")); - fireProjectInsertProgress(succeeded, failed, fatal, total, finalized); - } - success |= "PutSuccessful".equals(messageName); - finished = (success && (finalURI != null)) || "PutFailed".equals(messageName) || messageName.endsWith("Error"); - } - } - - /* post-insert work */ - if (success) { - @SuppressWarnings("null") - String editionPart = finalURI.substring(finalURI.lastIndexOf('/') + 1); - int newEdition = Integer.parseInt(editionPart); - project.setEdition(newEdition); - project.setLastInsertionTime(System.currentTimeMillis()); - project.onSuccessfulInsert(); - } - fireProjectInsertFinished(success, cancelled ? new AbortedException() : (disconnected ? new IOException("Connection terminated") : null)); - } - - // - // INTERFACE FileScannerListener - // - - /** - * {@inheritDoc} - */ - public void fileScannerFinished(FileScanner fileScanner) { - if (!fileScanner.isError()) { - new Thread(this).start(); - } else { - fireProjectInsertFinished(false, null); - } - fileScanner.removeFileScannerListener(this); - } - - /** - * Container class that collects all warnings and errors that occured during - * {@link ProjectInserter#validateProject(Project) project validation}. - * - * @author David ‘Bombe’ Roden - */ - public static class CheckReport implements Iterable { - - /** The issures that occured. */ - private final List issues = new ArrayList(); - - /** - * Adds an issue. - * - * @param issue - * The issue to add - */ - public void addIssue(Issue issue) { - issues.add(issue); - } - - /** - * Creates an {@link Issue} from the given error key and fatality flag - * and {@link #addIssue(Issue) adds} it. - * - * @param errorKey - * The error key - * @param fatal - * {@code true} if the error is fatal, {@code false} if only - * a warning should be generated - * @param parameters - * Any additional parameters - */ - public void addIssue(String errorKey, boolean fatal, String... parameters) { - addIssue(new Issue(errorKey, fatal, parameters)); - } - - /** - * {@inheritDoc} - */ - public Iterator iterator() { - return issues.iterator(); - } - - /** - * Returns whether this check report does not contain any errors. - * - * @return {@code true} if this check report does not contain any - * errors, {@code false} if this check report does contain - * errors - */ - public boolean isEmpty() { - return issues.isEmpty(); - } - - /** - * Returns the number of issues in this check report. - * - * @return The number of issues - */ - public int size() { - return issues.size(); - } - - } - - /** - * Container class for a single issue. An issue contains an error key - * that describes the error, and a fatality flag that determines whether - * the insert has to be aborted (if the flag is {@code true}) or if it - * can still be performed and only a warning should be generated (if the - * flag is {@code false}). - * - * @author David ‘Bombe’ - * Roden - */ - public static class Issue { - - /** The error key. */ - private final String errorKey; - - /** The fatality flag. */ - private final boolean fatal; - - /** Additional parameters. */ - private String[] parameters; - - /** - * Creates a new issue. - * - * @param errorKey - * The error key - * @param fatal - * The fatality flag - * @param parameters - * Any additional parameters - */ - protected Issue(String errorKey, boolean fatal, String... parameters) { - this.errorKey = errorKey; - this.fatal = fatal; - this.parameters = parameters; - } - - /** - * Returns the key of the encountered error. - * - * @return The error key - */ - public String getErrorKey() { - return errorKey; - } - - /** - * Returns whether the issue is fatal and the insert has to be - * aborted. Otherwise only a warning should be shown. - * - * @return {@code true} if the insert needs to be aborted, {@code - * false} otherwise - */ - public boolean isFatal() { - return fatal; - } - - /** - * Returns any additional parameters. - * - * @return The additional parameters - */ - public String[] getParameters() { - return parameters; - } - - } - -} diff --git a/src/de/todesbaum/jsite/application/UpdateChecker.java b/src/de/todesbaum/jsite/application/UpdateChecker.java deleted file mode 100644 index c49f166..0000000 --- a/src/de/todesbaum/jsite/application/UpdateChecker.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * jSite - UpdateChecker.java - Copyright © 2008–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; - -import de.todesbaum.jsite.main.Main; -import de.todesbaum.jsite.main.Version; -import de.todesbaum.util.freenet.fcp2.Client; -import de.todesbaum.util.freenet.fcp2.ClientGet; -import de.todesbaum.util.freenet.fcp2.Connection; -import de.todesbaum.util.freenet.fcp2.Message; -import de.todesbaum.util.freenet.fcp2.Persistence; -import de.todesbaum.util.freenet.fcp2.ReturnType; -import de.todesbaum.util.freenet.fcp2.Verbosity; -import de.todesbaum.util.io.Closer; - -/** - * Checks for newer versions of jSite. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class UpdateChecker implements Runnable { - - /** The logger. */ - private static final Logger logger = Logger.getLogger(UpdateChecker.class.getName()); - - /** Counter for connection names. */ - private static int counter = 0; - - /** The edition for the update check URL. */ - private static final int UPDATE_EDITION = 17; - - /** The URL for update checks. */ - private static final String UPDATE_KEY = "USK@e3myoFyp5avg6WYN16ImHri6J7Nj8980Fm~aQe4EX1U,QvbWT0ImE0TwLODTl7EoJx2NBnwDxTbLTE6zkB-eGPs,AQACAAE"; - - /** Object used for synchronization. */ - private final Object syncObject = new Object(); - - /** Update listeners. */ - private final List updateListeners = new ArrayList(); - - /** Whether the main thread should stop. */ - private boolean shouldStop = false; - - /** Current last found edition of update key. */ - private int lastUpdateEdition = UPDATE_EDITION; - - /** Last found version. */ - private Version lastVersion = Main.getVersion(); - - /** The freenet interface. */ - private final Freenet7Interface freenetInterface; - - /** - * Creates a new update checker that uses the given frame as its parent and - * communications via the given freenet interface. - * - * @param freenetInterface - * The freenet interface - */ - public UpdateChecker(Freenet7Interface freenetInterface) { - this.freenetInterface = freenetInterface; - } - - // - // EVENT LISTENER MANAGEMENT - // - - /** - * Adds an update listener to the list of registered listeners. - * - * @param updateListener - * The update listener to add - */ - public void addUpdateListener(UpdateListener updateListener) { - updateListeners.add(updateListener); - } - - /** - * Removes the given listener from the list of registered listeners. - * - * @param updateListener - * The update listener to remove - */ - public void removeUpdateListener(UpdateListener updateListener) { - updateListeners.remove(updateListener); - } - - /** - * Notifies all listeners that a version was found. - * - * @param foundVersion - * The version that was found - * @param versionTimestamp - * The timestamp of the version - */ - protected void fireUpdateFound(Version foundVersion, long versionTimestamp) { - for (UpdateListener updateListener : updateListeners) { - updateListener.foundUpdateData(foundVersion, versionTimestamp); - } - } - - // - // ACCESSORS - // - - /** - * Returns the latest version that was found. - * - * @return The latest found version - */ - public Version getLatestVersion() { - return lastVersion; - } - - // - // ACTIONS - // - - /** - * Starts the update checker. - */ - public void start() { - new Thread(this).start(); - } - - /** - * Stops the update checker. - */ - public void stop() { - synchronized (syncObject) { - shouldStop = true; - syncObject.notifyAll(); - } - } - - // - // PRIVATE METHODS - // - - /** - * Returns whether the update checker should stop. - * - * @return true if the update checker should stop, - * false otherwise - */ - private boolean shouldStop() { - synchronized (syncObject) { - return shouldStop; - } - } - - /** - * Creates the URI of the update file for the given edition. - * - * @param edition - * The edition number - * @return The URI for the update file for the given edition - */ - private String constructUpdateKey(int edition) { - return UPDATE_KEY + "/jSite/" + edition + "/jSite.properties"; - } - - // - // INTERFACE Runnable - // - - /** - * {@inheritDoc} - */ - public void run() { - Connection connection = freenetInterface.getConnection("jSite-" + ++counter + "-UpdateChecker"); - try { - connection.connect(); - } catch (IOException e1) { - e1.printStackTrace(); - } - Client client = new Client(connection); - boolean checkNow = false; - int currentEdition = lastUpdateEdition; - while (!shouldStop()) { - checkNow = false; - logger.log(Level.FINE, "Trying " + constructUpdateKey(currentEdition)); - ClientGet clientGet = new ClientGet("get-update-key"); - clientGet.setUri(constructUpdateKey(currentEdition)); - clientGet.setPersistence(Persistence.CONNECTION); - clientGet.setReturnType(ReturnType.direct); - clientGet.setVerbosity(Verbosity.ALL); - try { - client.execute(clientGet); - boolean stop = false; - while (!stop) { - Message message = client.readMessage(); - logger.log(Level.FINEST, "Received message: " + message); - if (message == null) { - break; - } - if ("GetFailed".equals(message.getName())) { - if ("27".equals(message.get("code"))) { - String editionString = message.get("redirecturi").split("/")[2]; - int editionNumber = -1; - try { - editionNumber = Integer.parseInt(editionString); - } catch (NumberFormatException nfe1) { - /* ignore. */ - } - if (editionNumber != -1) { - logger.log(Level.INFO, "Found new edition " + editionNumber); - currentEdition = editionNumber; - lastUpdateEdition = editionNumber; - checkNow = true; - break; - } - } - } - if ("AllData".equals(message.getName())) { - logger.log(Level.FINE, "Update data found."); - InputStream dataInputStream = null; - Properties properties = new Properties(); - try { - dataInputStream = message.getPayloadInputStream(); - properties.load(dataInputStream); - } finally { - Closer.close(dataInputStream); - } - - String foundVersionString = properties.getProperty("jSite.Version"); - if (foundVersionString != null) { - Version foundVersion = Version.parse(foundVersionString); - if (foundVersion != null) { - lastVersion = foundVersion; - String versionTimestampString = properties.getProperty("jSite.Date"); - logger.log(Level.FINEST, "Version timestamp: " + versionTimestampString); - long versionTimestamp = -1; - try { - versionTimestamp = Long.parseLong(versionTimestampString); - } catch (NumberFormatException nfe1) { - /* ignore. */ - } - fireUpdateFound(foundVersion, versionTimestamp); - stop = true; - checkNow = true; - ++currentEdition; - } - } - } - } - } catch (IOException e) { - logger.log(Level.INFO, "Got IOException: " + e.getMessage()); - e.printStackTrace(); - } - if (!checkNow && !shouldStop()) { - synchronized (syncObject) { - try { - syncObject.wait(15 * 60 * 1000); - } catch (InterruptedException ie1) { - /* ignore. */ - } - } - } - } - } - -} diff --git a/src/de/todesbaum/jsite/application/UpdateListener.java b/src/de/todesbaum/jsite/application/UpdateListener.java deleted file mode 100644 index a37440c..0000000 --- a/src/de/todesbaum/jsite/application/UpdateListener.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * jSite - UpdateListener.java - Copyright © 2008–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.application; - -import java.util.EventListener; - -import de.todesbaum.jsite.main.Version; - -/** - * Listener interface for objects that want to be notified when update data was - * found. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public interface UpdateListener extends EventListener { - - /** - * Notifies a listener that data for the given version was found. - * - * @param foundVersion - * The version that was found - * @param versionTimestamp - * The timestamp of the version, or -1 if the - * timestamp is unknown - */ - public void foundUpdateData(Version foundVersion, long versionTimestamp); - -} diff --git a/src/de/todesbaum/jsite/gui/FileScanner.java b/src/de/todesbaum/jsite/gui/FileScanner.java deleted file mode 100644 index 1790fbf..0000000 --- a/src/de/todesbaum/jsite/gui/FileScanner.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * jSite - FileScanner.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.io.File; -import java.io.FileFilter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.DigestOutputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; - -import de.todesbaum.jsite.application.Project; -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.util.io.Closer; -import de.todesbaum.util.io.StreamCopier; - -/** - * Scans the local path of a project anychronously and returns the list of found - * files as an event. - * - * @see Project#getLocalPath() - * @see FileScannerListener#fileScannerFinished(FileScanner) - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -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(); - - /** The project to scan. */ - private final Project project; - - /** The list of found files. */ - private List files; - - /** Wether there was an error. */ - private boolean error = false; - - /** - * Creates a new file scanner for the given project. - * - * @param project - * The project whose files to scan - */ - public FileScanner(Project project) { - this.project = project; - } - - /** - * Adds the given listener to the list of listeners. - * - * @param fileScannerListener - * The listener to add - */ - public void addFileScannerListener(FileScannerListener fileScannerListener) { - fileScannerListeners.add(fileScannerListener); - } - - /** - * Removes the given listener from the list of listeners. - * - * @param fileScannerListener - * The listener to remove - */ - public void removeFileScannerListener(FileScannerListener fileScannerListener) { - fileScannerListeners.remove(fileScannerListener); - } - - /** - * Notifies all listeners that the file scan finished. - */ - protected void fireFileScannerFinished() { - for (FileScannerListener fileScannerListener : new ArrayList(fileScannerListeners)) { - fileScannerListener.fileScannerFinished(this); - } - } - - /** - * {@inheritDoc} - *

- * Scans all available files in the project’s local path and emits an event - * when finished. - * - * @see FileScannerListener#fileScannerFinished(FileScanner) - */ - public void run() { - files = new ArrayList(); - error = false; - try { - scanFiles(new File(project.getLocalPath()), files); - Collections.sort(files); - } catch (IOException ioe1) { - error = true; - } - fireFileScannerFinished(); - } - - /** - * Returns whether there was an error scanning for files. - * - * @return true if there was an error, false - * otherwise - */ - public boolean isError() { - return error; - } - - /** - * Returns the list of found files. - * - * @return The list of found files - */ - public List getFiles() { - return files; - } - - /** - * Recursively scans a directory and adds all found files to the given list. - * - * @param rootDir - * The directory to scan - * @param fileList - * The list to which to add the found files - * @throws IOException - * if an I/O error occurs - */ - private void scanFiles(File rootDir, List fileList) throws IOException { - File[] files = rootDir.listFiles(new FileFilter() { - - @SuppressWarnings("synthetic-access") - public boolean accept(File file) { - return !project.isIgnoreHiddenFiles() || !file.isHidden(); - } - }); - if (files == null) { - throw new IOException(I18n.getMessage("jsite.file-scanner.can-not-read-directory")); - } - for (File file : files) { - if (file.isDirectory()) { - scanFiles(file, fileList); - continue; - } - String filename = project.shortenFilename(file).replace('\\', '/'); - String hash = hashFile(project.getLocalPath(), filename); - fileList.add(new ScannedFile(filename, hash)); - } - } - - /** - * Hashes the given file. - * - * @param path - * The path of the project - * @param filename - * The name of the file, relative to the project path - * @return The hash of the file - */ - @SuppressWarnings("synthetic-access") - private static String hashFile(String path, String filename) { - InputStream fileInputStream = null; - DigestOutputStream digestOutputStream = null; - File file = new File(path, filename); - try { - fileInputStream = new FileInputStream(file); - digestOutputStream = new DigestOutputStream(new NullOutputStream(), MessageDigest.getInstance("SHA-256")); - StreamCopier.copy(fileInputStream, digestOutputStream, file.length()); - return toHex(digestOutputStream.getMessageDigest().digest()); - } catch (NoSuchAlgorithmException nsae1) { - logger.log(Level.WARNING, "Could not get SHA-256 digest!", nsae1); - } catch (IOException ioe1) { - logger.log(Level.WARNING, "Could not read file!", ioe1); - } finally { - Closer.close(digestOutputStream); - Closer.close(fileInputStream); - } - return toHex(new byte[32]); - } - - /** - * Converts the given byte array into a hexadecimal string. - * - * @param array - * The array to convert - * @return The hexadecimal string - */ - private static String toHex(byte[] array) { - StringBuilder hexString = new StringBuilder(array.length * 2); - for (byte b : array) { - hexString.append("0123456789abcdef".charAt((b >>> 4) & 0x0f)).append("0123456789abcdef".charAt(b & 0xf)); - } - return hexString.toString(); - } - - /** - * {@link OutputStream} that discards all written bytes. - * - * @author David ‘Bombe’ Roden <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); - } - - } - -} diff --git a/src/de/todesbaum/jsite/gui/FileScannerListener.java b/src/de/todesbaum/jsite/gui/FileScannerListener.java deleted file mode 100644 index 63e2c3d..0000000 --- a/src/de/todesbaum/jsite/gui/FileScannerListener.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * jSite - FileScannerListener.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.util.EventListener; - -/** - * Listener interface for objects that want to be notified when scanning a - * project’s local path has finished. - * - * @see FileScanner - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public interface FileScannerListener extends EventListener { - - /** - * Notifies a listener that scanning a project’s local path has finished. - * - * @param fileScanner - * The file scanner that finished - */ - public void fileScannerFinished(FileScanner fileScanner); - -} \ No newline at end of file diff --git a/src/de/todesbaum/jsite/gui/NodeManagerListener.java b/src/de/todesbaum/jsite/gui/NodeManagerListener.java deleted file mode 100644 index 760c967..0000000 --- a/src/de/todesbaum/jsite/gui/NodeManagerListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * jSite - NodeManagerListener.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.util.EventListener; - -import de.todesbaum.jsite.application.Node; - -/** - * Listener interface for objects that want to be notified if the node - * configuration changes. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public interface NodeManagerListener extends EventListener { - - /** - * Notifies a listener that the node configuration was changed. - * - * @param nodes - * The new list of nodes - */ - public void nodesUpdated(Node[] nodes); - - /** - * Notifies a listener that the selected node has changed. - * - * @param node - * The new selected node - */ - public void nodeSelected(Node node); - -} diff --git a/src/de/todesbaum/jsite/gui/NodeManagerPage.java b/src/de/todesbaum/jsite/gui/NodeManagerPage.java deleted file mode 100644 index 8e44129..0000000 --- a/src/de/todesbaum/jsite/gui/NodeManagerPage.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * jSite - NodeManagerPage.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.DefaultListModel; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.border.EmptyBorder; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; - -import de.todesbaum.jsite.application.Node; -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.jsite.i18n.I18nContainer; -import de.todesbaum.util.swing.TLabel; -import de.todesbaum.util.swing.TWizard; -import de.todesbaum.util.swing.TWizardPage; - -/** - * Wizard page that lets the user edit his nodes. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class NodeManagerPage extends TWizardPage implements ListSelectionListener, DocumentListener, ChangeListener { - - /** List of node manager listeners. */ - private List nodeManagerListeners = new ArrayList(); - - /** The “add node” action. */ - protected Action addNodeAction; - - /** The “delete node” action. */ - protected Action deleteNodeAction; - - /** The node list model. */ - private DefaultListModel nodeListModel; - - /** The node list. */ - private JList nodeList; - - /** The node name textfield. */ - private JTextField nodeNameTextField; - - /** The node hostname textfield. */ - private JTextField nodeHostnameTextField; - - /** The spinner for the node port. */ - private JSpinner nodePortSpinner; - - /** - * Creates a new node manager wizard page. - * - * @param wizard - * The wizard this page belongs to - */ - public NodeManagerPage(final TWizard wizard) { - super(wizard); - pageInit(); - setHeading(I18n.getMessage("jsite.node-manager.heading")); - setDescription(I18n.getMessage("jsite.node-manager.description")); - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - setHeading(I18n.getMessage("jsite.node-manager.heading")); - setDescription(I18n.getMessage("jsite.node-manager.description")); - } - }); - } - - /** - * Adds a listener for node manager events. - * - * @param nodeManagerListener - * The listener to add - */ - public void addNodeManagerListener(NodeManagerListener nodeManagerListener) { - nodeManagerListeners.add(nodeManagerListener); - } - - /** - * Removes a listener for node manager events. - * - * @param nodeManagerListener - * The listener to remove - */ - public void removeNodeManagerListener(NodeManagerListener nodeManagerListener) { - nodeManagerListeners.remove(nodeManagerListener); - } - - /** - * Notifies all listeners that the node configuration has changed. - * - * @param nodes - * The new list of nodes - */ - protected void fireNodesUpdated(Node[] nodes) { - for (NodeManagerListener nodeManagerListener : nodeManagerListeners) { - nodeManagerListener.nodesUpdated(nodes); - } - } - - /** - * Notifies all listeners that a new node was selected. - * - * @param node - * The newly selected node - */ - protected void fireNodeSelected(Node node) { - for (NodeManagerListener nodeManagerListener : nodeManagerListeners) { - nodeManagerListener.nodeSelected(node); - } - } - - /** - * Creates all actions. - */ - private void createActions() { - addNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.add-node")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - addNode(); - } - }; - - deleteNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.delete-node")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - deleteNode(); - } - }; - deleteNodeAction.setEnabled(false); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - addNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.add-node")); - deleteNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.delete-node")); - } - }); - } - - /** - * Initializes the page and all components in it. - */ - private void pageInit() { - createActions(); - nodeListModel = new DefaultListModel(); - nodeList = new JList(nodeListModel); - nodeList.setName("node-list"); - nodeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - nodeList.addListSelectionListener(this); - nodeList.setPreferredSize(new Dimension(250, -1)); - - nodeNameTextField = new JTextField(""); - nodeNameTextField.getDocument().putProperty("Name", "node-name"); - nodeNameTextField.getDocument().addDocumentListener(this); - nodeNameTextField.setEnabled(false); - - nodeHostnameTextField = new JTextField("localhost"); - nodeHostnameTextField.getDocument().putProperty("Name", "node-hostname"); - nodeHostnameTextField.getDocument().addDocumentListener(this); - nodeHostnameTextField.setEnabled(false); - - nodePortSpinner = new JSpinner(new SpinnerNumberModel(9481, 1, 65535, 1)); - nodePortSpinner.setName("node-port"); - nodePortSpinner.addChangeListener(this); - nodePortSpinner.setEnabled(false); - - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12)); - buttonPanel.setBorder(new EmptyBorder(-12, -12, -12, -12)); - buttonPanel.add(new JButton(addNodeAction)); - buttonPanel.add(new JButton(deleteNodeAction)); - - JPanel centerPanel = new JPanel(new BorderLayout()); - JPanel nodeInformationPanel = new JPanel(new GridBagLayout()); - centerPanel.add(nodeInformationPanel, BorderLayout.PAGE_START); - nodeInformationPanel.add(buttonPanel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); - final JLabel nodeInformationLabel = new JLabel("" + I18n.getMessage("jsite.node-manager.node-information") + ""); - nodeInformationPanel.add(nodeInformationLabel, new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); - final TLabel nodeNameLabel = new TLabel(I18n.getMessage("jsite.node-manager.name") + ":", KeyEvent.VK_N, nodeNameTextField); - nodeInformationPanel.add(nodeNameLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - nodeInformationPanel.add(nodeNameTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - final TLabel nodeHostnameLabel = new TLabel(I18n.getMessage("jsite.node-manager.hostname") + ":", KeyEvent.VK_H, nodeHostnameTextField); - nodeInformationPanel.add(nodeHostnameLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - nodeInformationPanel.add(nodeHostnameTextField, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - final TLabel nodePortLabel = new TLabel(I18n.getMessage("jsite.node-manager.port") + ":", KeyEvent.VK_P, nodePortSpinner); - nodeInformationPanel.add(nodePortLabel, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - nodeInformationPanel.add(nodePortSpinner, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0)); - - setLayout(new BorderLayout(12, 12)); - add(new JScrollPane(nodeList), BorderLayout.LINE_START); - add(centerPanel, BorderLayout.CENTER); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - nodeInformationLabel.setText("" + I18n.getMessage("jsite.node-manager.node-information") + ""); - nodeNameLabel.setText(I18n.getMessage("jsite.node-manager.name") + ":"); - nodeHostnameLabel.setText(I18n.getMessage("jsite.node-manager.hostname") + ":"); - nodePortLabel.setText(I18n.getMessage("jsite.node-manager.port") + ":"); - } - }); - } - - /** - * {@inheritDoc} - */ - @Override - public void pageAdded(TWizard wizard) { - this.wizard.setNextEnabled(nodeListModel.getSize() > 0); - this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); - this.wizard.setNextName(I18n.getMessage("jsite.wizard.next")); - this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); - } - - /** - * Sets the node list. - * - * @param nodes - * The list of nodes - */ - public void setNodes(Node[] nodes) { - nodeListModel.clear(); - for (Node node : nodes) { - nodeListModel.addElement(node); - } - nodeList.repaint(); - fireNodesUpdated(nodes); - } - - /** - * Returns the node list. - * - * @return The list of nodes - */ - public Node[] getNodes() { - Node[] returnNodes = new Node[nodeListModel.getSize()]; - for (int nodeIndex = 0, nodeCount = nodeListModel.getSize(); nodeIndex < nodeCount; nodeIndex++) { - returnNodes[nodeIndex] = (Node) nodeListModel.get(nodeIndex); - } - return returnNodes; - } - - /** - * Returns the currently selected node. - * - * @return The selected node, or null if no node is selected - */ - private Node getSelectedNode() { - return (Node) nodeList.getSelectedValue(); - } - - /** - * Updates node name or hostname when the user types into the textfields. - * - * @see #insertUpdate(DocumentEvent) - * @see #removeUpdate(DocumentEvent) - * @see #changedUpdate(DocumentEvent) - * @see DocumentListener - * @param documentEvent - * The document event - */ - private void updateTextField(DocumentEvent documentEvent) { - Node node = getSelectedNode(); - if (node == null) { - return; - } - Document document = documentEvent.getDocument(); - String documentText = null; - try { - documentText = document.getText(0, document.getLength()); - } catch (BadLocationException ble1) { - /* ignore. */ - } - if (documentText == null) { - return; - } - String documentName = (String) document.getProperty("Name"); - if ("node-name".equals(documentName)) { - node.setName(documentText); - nodeList.repaint(); - fireNodesUpdated(getNodes()); - } else if ("node-hostname".equals(documentName)) { - node.setHostname(documentText); - nodeList.repaint(); - fireNodesUpdated(getNodes()); - } - } - - // - // ACTIONS - // - - /** - * Adds a new node to the list of nodes. - */ - private void addNode() { - Node node = new Node("localhost", 9481, I18n.getMessage("jsite.node-manager.new-node")); - nodeListModel.addElement(node); - deleteNodeAction.setEnabled(nodeListModel.size() > 1); - wizard.setNextEnabled(true); - fireNodesUpdated(getNodes()); - } - - /** - * Deletes the currently selected node from the list of nodes. - */ - private void deleteNode() { - Node node = getSelectedNode(); - if (node == null) { - return; - } - if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.node-manager.delete-node.warning"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.CANCEL_OPTION) { - return; - } - int nodeIndex = nodeListModel.indexOf(node); - nodeListModel.removeElement(node); - nodeList.repaint(); - fireNodeSelected((Node) nodeListModel.get(Math.min(nodeIndex, nodeListModel.size() - 1))); - fireNodesUpdated(getNodes()); - deleteNodeAction.setEnabled(nodeListModel.size() > 1); - wizard.setNextEnabled(nodeListModel.size() > 0); - } - - // - // INTERFACE ListSelectionListener - // - - /** - * {@inheritDoc} - */ - @SuppressWarnings("null") - public void valueChanged(ListSelectionEvent e) { - Object source = e.getSource(); - if (source instanceof JList) { - JList sourceList = (JList) source; - if ("node-list".equals(sourceList.getName())) { - Node node = (Node) sourceList.getSelectedValue(); - boolean enabled = (node != null); - nodeNameTextField.setEnabled(enabled); - nodeHostnameTextField.setEnabled(enabled); - nodePortSpinner.setEnabled(enabled); - deleteNodeAction.setEnabled(enabled && (nodeListModel.size() > 1)); - if (enabled) { - nodeNameTextField.setText(node.getName()); - nodeHostnameTextField.setText(node.getHostname()); - nodePortSpinner.setValue(node.getPort()); - } else { - nodeNameTextField.setText(""); - nodeHostnameTextField.setText("localhost"); - nodePortSpinner.setValue(9481); - } - } - } - } - - // - // INTERFACE DocumentListener - // - - /** - * {@inheritDoc} - */ - public void insertUpdate(DocumentEvent e) { - updateTextField(e); - } - - /** - * {@inheritDoc} - */ - public void removeUpdate(DocumentEvent e) { - updateTextField(e); - } - - /** - * {@inheritDoc} - */ - public void changedUpdate(DocumentEvent e) { - updateTextField(e); - } - - // - // INTERFACE ChangeListener - // - - /** - * {@inheritDoc} - */ - public void stateChanged(ChangeEvent e) { - Object source = e.getSource(); - Node selectedNode = getSelectedNode(); - if (selectedNode == null) { - return; - } - if (source instanceof JSpinner) { - JSpinner sourceSpinner = (JSpinner) source; - if ("node-port".equals(sourceSpinner.getName())) { - selectedNode.setPort((Integer) sourceSpinner.getValue()); - fireNodeSelected(selectedNode); - nodeList.repaint(); - } - } - } - -} diff --git a/src/de/todesbaum/jsite/gui/PreferencesPage.java b/src/de/todesbaum/jsite/gui/PreferencesPage.java deleted file mode 100644 index 5cf258a..0000000 --- a/src/de/todesbaum/jsite/gui/PreferencesPage.java +++ /dev/null @@ -1,519 +0,0 @@ -/* - * jSite - PreferencesPage.java - Copyright © 2009–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.awt.BorderLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.BorderFactory; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JRadioButton; -import javax.swing.JTextField; - -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.jsite.i18n.I18nContainer; -import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; -import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; -import de.todesbaum.util.freenet.fcp2.PriorityClass; -import de.todesbaum.util.swing.TWizard; -import de.todesbaum.util.swing.TWizardPage; - -/** - * Page that shows some preferences that are valid for the complete application. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class PreferencesPage extends TWizardPage { - - /** Select default temp directory action. */ - private Action selectDefaultTempDirectoryAction; - - /** Select custom temp directory action. */ - private Action selectCustomTempDirectoryAction; - - /** Action that chooses a new temp directory. */ - private Action chooseTempDirectoryAction; - - /** Action when selecting “next to JAR file.” */ - private Action nextToJarFileAction; - - /** Action when selecting “home directory.” */ - private Action homeDirectoryAction; - - /** Action when selecting “custom directory.” */ - private Action customDirectoryAction; - - /** Action when selecting “use early encode.” */ - private Action useEarlyEncodeAction; - - /** Action when a priority was selected. */ - private Action priorityAction; - - /** The text field containing the directory. */ - private JTextField tempDirectoryTextField; - - /** The temp directory. */ - private String tempDirectory; - - /** The configuration location. */ - private ConfigurationLocation configurationLocation; - - /** Whether to use “early encode.” */ - private boolean useEarlyEncode; - - /** The prioriy for inserts. */ - private PriorityClass priority; - - /** The “default” button. */ - private JRadioButton defaultTempDirectory; - - /** The “custom” button. */ - private JRadioButton customTempDirectory; - - /** The “next to JAR file” checkbox. */ - private JRadioButton nextToJarFile; - - /** The “home directory” checkbox. */ - private JRadioButton homeDirectory; - - /** The “custom directory” checkbox. */ - private JRadioButton customDirectory; - - /** The “use early encode” checkbox. */ - private JCheckBox useEarlyEncodeCheckBox; - - /** The insert priority select box. */ - private JComboBox insertPriorityComboBox; - - /** The manifest putter select box. */ - private JComboBox manifestPutterComboBox; - - /** - * Creates a new “preferences” page. - * - * @param wizard - * The wizard this page belongs to - */ - public PreferencesPage(TWizard wizard) { - super(wizard); - pageInit(); - setHeading(I18n.getMessage("jsite.preferences.heading")); - setDescription(I18n.getMessage("jsite.preferences.description")); - I18nContainer.getInstance().registerRunnable(new Runnable() { - - /** - * {@inheritDoc} - */ - public void run() { - setHeading(I18n.getMessage("jsite.preferences.heading")); - setDescription(I18n.getMessage("jsite.preferences.description")); - } - }); - } - - // - // ACCESSORS - // - - /** - * Returns the temp directory. - * - * @return The temp directory, or {@code null} to use the default temp - * directory - */ - public String getTempDirectory() { - return tempDirectory; - } - - /** - * Sets the temp directory. - * - * @param tempDirectory - * The temp directory, or {@code null} to use the default temp - * directory - */ - public void setTempDirectory(String tempDirectory) { - this.tempDirectory = tempDirectory; - tempDirectoryTextField.setText((tempDirectory != null) ? tempDirectory : ""); - if (tempDirectory != null) { - customTempDirectory.setSelected(true); - chooseTempDirectoryAction.setEnabled(true); - } else { - defaultTempDirectory.setSelected(true); - } - } - - /** - * Returns the configuration location. - * - * @return The configuration location - */ - public ConfigurationLocation getConfigurationLocation() { - return configurationLocation; - } - - /** - * Sets the configuration location. - * - * @param configurationLocation - * The configuration location - */ - public void setConfigurationLocation(ConfigurationLocation configurationLocation) { - this.configurationLocation = configurationLocation; - switch (configurationLocation) { - case NEXT_TO_JAR_FILE: - nextToJarFile.setSelected(true); - break; - case HOME_DIRECTORY: - homeDirectory.setSelected(true); - break; - case CUSTOM: - customDirectory.setSelected(true); - break; - } - } - - /** - * Sets whether it is possible to select the “next to JAR file” option for - * the configuration location. - * - * @param nextToJarFile - * {@code true} if the configuration file can be saved next to - * the JAR file, {@code false} otherwise - */ - public void setHasNextToJarConfiguration(boolean nextToJarFile) { - this.nextToJarFile.setEnabled(nextToJarFile); - } - - /** - * Sets whether it is possible to select the “custom location” option for - * the configuration location. - * - * @param customDirectory - * {@code true} if the configuration file can be saved to a - * custom location, {@code false} otherwise - */ - public void setHasCustomConfiguration(boolean customDirectory) { - this.customDirectory.setEnabled(customDirectory); - } - - /** - * Returns whether to use the “early encode“ flag for the insert. - * - * @return {@code true} to set the “early encode” flag for the insert, - * {@code false} otherwise - */ - public boolean useEarlyEncode() { - return useEarlyEncode; - } - - /** - * Sets whether to use the “early encode“ flag for the insert. - * - * @param useEarlyEncode - * {@code true} to set the “early encode” flag for the insert, - * {@code false} otherwise - */ - public void setUseEarlyEncode(boolean useEarlyEncode) { - useEarlyEncodeCheckBox.setSelected(useEarlyEncode); - } - - /** - * Returns the configured insert priority. - * - * @return The insert priority - */ - public PriorityClass getPriority() { - return priority; - } - - /** - * Sets the insert priority. - * - * @param priority - * The insert priority - */ - public void setPriority(PriorityClass priority) { - insertPriorityComboBox.setSelectedItem(priority); - } - - /** - * Returns the selected manifest putter. - * - * @return The selected manifest putter - */ - public ManifestPutter getManifestPutter() { - return (ManifestPutter) manifestPutterComboBox.getSelectedItem(); - } - - /** - * Sets the manifest putter. - * - * @param manifestPutter - * The manifest putter - */ - public void setManifestPutter(ManifestPutter manifestPutter) { - manifestPutterComboBox.setSelectedItem(manifestPutter); - } - - /** - * {@inheritDoc} - */ - @Override - public void pageAdded(TWizard wizard) { - super.pageAdded(wizard); - this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes")); - this.wizard.setNextName(I18n.getMessage("jsite.wizard.next")); - this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); - this.wizard.setNextEnabled(false); - } - - // - // PRIVATE METHODS - // - - /** - * Initializes this page. - */ - private void pageInit() { - createActions(); - setLayout(new BorderLayout(12, 12)); - add(createPreferencesPanel(), BorderLayout.CENTER); - } - - /** - * Creates all actions. - */ - private void createActions() { - selectDefaultTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.default")) { - - /** - * {@inheritDoc} - */ - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - selectDefaultTempDirectory(); - } - }; - selectCustomTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.custom")) { - - /** - * {@inheritDoc} - */ - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - selectCustomTempDirectory(); - } - }; - chooseTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.choose")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent e) { - chooseTempDirectory(); - } - }; - nextToJarFileAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.jar")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionevent) { - configurationLocation = ConfigurationLocation.NEXT_TO_JAR_FILE; - } - }; - homeDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.home")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionevent) { - configurationLocation = ConfigurationLocation.HOME_DIRECTORY; - } - }; - customDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.custom")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - configurationLocation = ConfigurationLocation.CUSTOM; - } - }; - useEarlyEncodeAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.use-early-encode")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - useEarlyEncode = useEarlyEncodeCheckBox.isSelected(); - } - }; - priorityAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.priority")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - priority = (PriorityClass) insertPriorityComboBox.getSelectedItem(); - } - }; - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - selectDefaultTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.default")); - selectCustomTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.custom")); - chooseTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.choose")); - nextToJarFileAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.jar")); - homeDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.home")); - customDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.custom")); - useEarlyEncodeAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.insert-options.use-early-encode")); - } - }); - } - - /** - * Creates the panel containing all preferences. - * - * @return The preferences panel - */ - private JPanel createPreferencesPanel() { - JPanel preferencesPanel = new JPanel(new GridBagLayout()); - preferencesPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); - - final JLabel tempDirectoryLabel = new JLabel("" + I18n.getMessage("jsite.preferences.temp-directory") + ""); - preferencesPanel.add(tempDirectoryLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); - - defaultTempDirectory = new JRadioButton(selectDefaultTempDirectoryAction); - preferencesPanel.add(defaultTempDirectory, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0)); - - customTempDirectory = new JRadioButton(selectCustomTempDirectoryAction); - preferencesPanel.add(customTempDirectory, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0)); - - ButtonGroup tempDirectoryButtonGroup = new ButtonGroup(); - defaultTempDirectory.getModel().setGroup(tempDirectoryButtonGroup); - customTempDirectory.getModel().setGroup(tempDirectoryButtonGroup); - - tempDirectoryTextField = new JTextField(); - tempDirectoryTextField.setEditable(false); - if (tempDirectory != null) { - tempDirectoryTextField.setText(tempDirectory); - customTempDirectory.setSelected(true); - } else { - defaultTempDirectory.setSelected(true); - } - chooseTempDirectoryAction.setEnabled(tempDirectory != null); - preferencesPanel.add(tempDirectoryTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); - - JButton chooseButton = new JButton(chooseTempDirectoryAction); - preferencesPanel.add(chooseButton, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); - - final JLabel configurationDirectoryLabel = new JLabel("" + 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() { - - /** - * {@inheritDoc} - */ - 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")); - } - }); - - return preferencesPanel; - } - - /** - * Activates the default temp directory radio button. - */ - private void selectDefaultTempDirectory() { - tempDirectoryTextField.setEnabled(false); - chooseTempDirectoryAction.setEnabled(false); - tempDirectory = null; - } - - /** - * Activates the custom temp directory radio button. - */ - private void selectCustomTempDirectory() { - tempDirectoryTextField.setEnabled(true); - chooseTempDirectoryAction.setEnabled(true); - if (tempDirectoryTextField.getText().length() == 0) { - chooseTempDirectory(); - if (tempDirectoryTextField.getText().length() == 0) { - defaultTempDirectory.setSelected(true); - } - } - } - - /** - * Lets the user choose a new temp directory. - */ - private void chooseTempDirectory() { - JFileChooser fileChooser = new JFileChooser(tempDirectory); - fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - int returnValue = fileChooser.showDialog(wizard, I18n.getMessage("jsite.preferences.temp-directory.choose.approve")); - if (returnValue == JFileChooser.CANCEL_OPTION) { - return; - } - tempDirectory = fileChooser.getSelectedFile().getPath(); - tempDirectoryTextField.setText(tempDirectory); - } - -} diff --git a/src/de/todesbaum/jsite/gui/ProjectFilesPage.java b/src/de/todesbaum/jsite/gui/ProjectFilesPage.java deleted file mode 100644 index 80bb52c..0000000 --- a/src/de/todesbaum/jsite/gui/ProjectFilesPage.java +++ /dev/null @@ -1,577 +0,0 @@ -/* - * jSite - ProjectFilesPage.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; -import java.text.MessageFormat; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; -import javax.swing.SwingUtilities; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; - -import de.todesbaum.jsite.application.FileOption; -import de.todesbaum.jsite.application.Project; -import de.todesbaum.jsite.gui.FileScanner.ScannedFile; -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.jsite.i18n.I18nContainer; -import de.todesbaum.util.mime.DefaultMIMETypes; -import de.todesbaum.util.swing.TLabel; -import de.todesbaum.util.swing.TWizard; -import de.todesbaum.util.swing.TWizardPage; - -/** - * Wizard page that lets the user manage the files of a project. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class ProjectFilesPage extends TWizardPage implements ActionListener, ListSelectionListener, DocumentListener, FileScannerListener { - - /** The project. */ - private Project project; - - /** The “scan files” action. */ - private Action scanAction; - - /** The “ignore hidden files” checkbox. */ - private JCheckBox ignoreHiddenFilesCheckBox; - - /** The list of project files. */ - private JList projectFileList; - - /** The “default file” checkbox. */ - private JCheckBox defaultFileCheckBox; - - /** The “insert” checkbox. */ - private JCheckBox fileOptionsInsertCheckBox; - - /** The “force insert” checkbox. */ - private JCheckBox fileOptionsForceInsertCheckBox; - - /** The “insert redirect” checkbox. */ - private JCheckBox fileOptionsInsertRedirectCheckBox; - - /** The “custom key” textfield. */ - private JTextField fileOptionsCustomKeyTextField; - - /** The “rename” check box. */ - private JCheckBox fileOptionsRenameCheckBox; - - /** The “new name” text field. */ - private JTextField fileOptionsRenameTextField; - - /** The “mime type” combo box. */ - private JComboBox fileOptionsMIMETypeComboBox; - - /** - * Creates a new project file page. - * - * @param wizard - * The wizard the page belongs to - */ - public ProjectFilesPage(final TWizard wizard) { - super(wizard); - pageInit(); - } - - /** - * Initializes the page and all its actions and components. - */ - private void pageInit() { - createActions(); - setLayout(new BorderLayout(12, 12)); - add(createProjectFilesPanel(), BorderLayout.CENTER); - } - - /** - * Creates all actions. - */ - private void createActions() { - scanAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.rescan")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionScan(); - } - }; - scanAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S); - scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip")); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - scanAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.rescan")); - scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip")); - } - }); - } - - /** - * {@inheritDoc} - */ - @Override - public void pageAdded(TWizard wizard) { - actionScan(); - this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); - this.wizard.setNextName(I18n.getMessage("jsite.project-files.insert-now")); - this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); - } - - /** - * Creates the panel contains the project file list and options. - * - * @return The created panel - */ - private JComponent createProjectFilesPanel() { - JPanel projectFilesPanel = new JPanel(new BorderLayout(12, 12)); - - projectFileList = new JList(); - projectFileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - projectFileList.setMinimumSize(new Dimension(250, projectFileList.getPreferredSize().height)); - projectFileList.addListSelectionListener(this); - - projectFilesPanel.add(new JScrollPane(projectFileList), BorderLayout.CENTER); - - JPanel fileOptionsAlignmentPanel = new JPanel(new BorderLayout(12, 12)); - projectFilesPanel.add(fileOptionsAlignmentPanel, BorderLayout.PAGE_END); - JPanel fileOptionsPanel = new JPanel(new GridBagLayout()); - fileOptionsAlignmentPanel.add(fileOptionsPanel, BorderLayout.PAGE_START); - - ignoreHiddenFilesCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.ignore-hidden-files")); - ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.ignore-hidden-files.tooltip")); - ignoreHiddenFilesCheckBox.setName("ignore-hidden-files"); - ignoreHiddenFilesCheckBox.addActionListener(this); - fileOptionsPanel.add(ignoreHiddenFilesCheckBox, new GridBagConstraints(0, 0, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - - fileOptionsPanel.add(new JButton(scanAction), new GridBagConstraints(0, 1, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); - - final JLabel fileOptionsLabel = new JLabel("" + I18n.getMessage("jsite.project-files.file-options") + ""); - fileOptionsPanel.add(fileOptionsLabel, new GridBagConstraints(0, 2, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); - - defaultFileCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.default")); - defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip")); - defaultFileCheckBox.setName("default-file"); - defaultFileCheckBox.addActionListener(this); - defaultFileCheckBox.setEnabled(false); - - fileOptionsPanel.add(defaultFileCheckBox, new GridBagConstraints(0, 3, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); - - fileOptionsInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.insert"), true); - fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip")); - fileOptionsInsertCheckBox.setName("insert"); - fileOptionsInsertCheckBox.setMnemonic(KeyEvent.VK_I); - fileOptionsInsertCheckBox.addActionListener(this); - fileOptionsInsertCheckBox.setEnabled(false); - - fileOptionsPanel.add(fileOptionsInsertCheckBox, new GridBagConstraints(0, 4, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - - fileOptionsForceInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.force-insert")); - fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip")); - fileOptionsForceInsertCheckBox.setName("force-insert"); - fileOptionsForceInsertCheckBox.setMnemonic(KeyEvent.VK_F); - fileOptionsForceInsertCheckBox.addActionListener(this); - fileOptionsForceInsertCheckBox.setEnabled(false); - - fileOptionsPanel.add(fileOptionsForceInsertCheckBox, new GridBagConstraints(0, 5, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - - fileOptionsCustomKeyTextField = new JTextField(45); - fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip")); - fileOptionsCustomKeyTextField.setEnabled(false); - fileOptionsCustomKeyTextField.getDocument().addDocumentListener(this); - - fileOptionsInsertRedirectCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.insert-redirect"), false); - fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip")); - fileOptionsInsertRedirectCheckBox.setName("insert-redirect"); - fileOptionsInsertRedirectCheckBox.setMnemonic(KeyEvent.VK_R); - fileOptionsInsertRedirectCheckBox.addActionListener(this); - fileOptionsInsertRedirectCheckBox.setEnabled(false); - - final TLabel customKeyLabel = new TLabel(I18n.getMessage("jsite.project-files.custom-key") + ":", KeyEvent.VK_K, fileOptionsCustomKeyTextField); - fileOptionsPanel.add(fileOptionsInsertRedirectCheckBox, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - fileOptionsPanel.add(customKeyLabel, new GridBagConstraints(1, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0)); - fileOptionsPanel.add(fileOptionsCustomKeyTextField, new GridBagConstraints(2, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - fileOptionsRenameCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.rename"), false); - fileOptionsRenameCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.rename.tooltip")); - fileOptionsRenameCheckBox.setName("rename"); - fileOptionsRenameCheckBox.setMnemonic(KeyEvent.VK_N); - fileOptionsRenameCheckBox.addActionListener(this); - fileOptionsRenameCheckBox.setEnabled(false); - - fileOptionsRenameTextField = new JTextField(); - fileOptionsRenameTextField.setEnabled(false); - fileOptionsRenameTextField.getDocument().addDocumentListener(new DocumentListener() { - - @SuppressWarnings("synthetic-access") - private void storeText(DocumentEvent documentEvent) { - FileOption fileOption = getSelectedFile(); - if (fileOption == null) { - /* no file selected. */ - return; - } - Document document = documentEvent.getDocument(); - int documentLength = document.getLength(); - try { - fileOption.setChangedName(document.getText(0, documentLength).trim()); - } catch (BadLocationException ble1) { - /* ignore, it should never happen. */ - } - } - - public void changedUpdate(DocumentEvent documentEvent) { - storeText(documentEvent); - } - - public void insertUpdate(DocumentEvent documentEvent) { - storeText(documentEvent); - } - - public void removeUpdate(DocumentEvent documentEvent) { - storeText(documentEvent); - } - - }); - - fileOptionsPanel.add(fileOptionsRenameCheckBox, new GridBagConstraints(0, 7, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - fileOptionsPanel.add(fileOptionsRenameTextField, new GridBagConstraints(2, 7, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - fileOptionsMIMETypeComboBox = new JComboBox(DefaultMIMETypes.getAllMIMETypes()); - fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip")); - fileOptionsMIMETypeComboBox.setName("project-files.mime-type"); - fileOptionsMIMETypeComboBox.addActionListener(this); - fileOptionsMIMETypeComboBox.setEditable(true); - fileOptionsMIMETypeComboBox.setEnabled(false); - - final TLabel mimeTypeLabel = new TLabel(I18n.getMessage("jsite.project-files.mime-type") + ":", KeyEvent.VK_M, fileOptionsMIMETypeComboBox); - fileOptionsPanel.add(mimeTypeLabel, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - fileOptionsPanel.add(fileOptionsMIMETypeComboBox, new GridBagConstraints(1, 8, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - ignoreHiddenFilesCheckBox.setText(I18n.getMessage("jsite.project-files.ignore-hidden-files")); - ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.projet-files.ignore-hidden-files.tooltip")); - fileOptionsLabel.setText("" + I18n.getMessage("jsite.project-files.file-options") + ""); - defaultFileCheckBox.setText(I18n.getMessage("jsite.project-files.default")); - defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip")); - fileOptionsInsertCheckBox.setText(I18n.getMessage("jsite.project-files.insert")); - fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip")); - fileOptionsForceInsertCheckBox.setText(I18n.getMessage("jsite.project-files.force-insert")); - fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip")); - fileOptionsInsertRedirectCheckBox.setText(I18n.getMessage("jsite.project-files.insert-redirect")); - fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip")); - fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip")); - customKeyLabel.setText(I18n.getMessage("jsite.project-files.custom-key") + ":"); - fileOptionsRenameCheckBox.setText("jsite.project-files.rename"); - fileOptionsRenameCheckBox.setToolTipText("jsite.project-files.rename.tooltip"); - fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip")); - mimeTypeLabel.setText(I18n.getMessage("jsite.project-files.mime-type") + ":"); - } - }); - - return projectFilesPanel; - } - - /** - * Sets the project whose files to manage. - * - * @param project - * The project whose files to manage - */ - public void setProject(final Project project) { - this.project = project; - setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName())); - setDescription(I18n.getMessage("jsite.project-files.description")); - ignoreHiddenFilesCheckBox.setSelected(project.isIgnoreHiddenFiles()); - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName())); - setDescription(I18n.getMessage("jsite.project-files.description")); - } - }); - } - - // - // ACTIONS - // - - /** - * Rescans the project’s files. - */ - private void actionScan() { - projectFileList.clearSelection(); - projectFileList.setListData(new Object[0]); - - wizard.setNextEnabled(false); - wizard.setPreviousEnabled(false); - wizard.setQuitEnabled(false); - - FileScanner fileScanner = new FileScanner(project); - fileScanner.addFileScannerListener(this); - new Thread(fileScanner).start(); - } - - /** - * {@inheritDoc} - *

- * Updates the file list. - */ - public void fileScannerFinished(FileScanner fileScanner) { - final boolean error = fileScanner.isError(); - if (!error) { - final List files = fileScanner.getFiles(); - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - projectFileList.setListData(files.toArray()); - projectFileList.clearSelection(); - } - }); - Set entriesToRemove = new HashSet(); - Iterator filenames = new HashSet(project.getFileOptions().keySet()).iterator(); - while (filenames.hasNext()) { - String filename = filenames.next(); - boolean found = false; - for (ScannedFile scannedFile : files) { - if (scannedFile.getFilename().equals(filename)) { - found = true; - break; - } - } - if (!found) { - entriesToRemove.add(filename); - } - } - for (String filename : entriesToRemove) { - project.setFileOption(filename, null); - } - } else { - JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.scan-error"), null, JOptionPane.ERROR_MESSAGE); - } - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - wizard.setPreviousEnabled(true); - wizard.setNextEnabled(!error); - wizard.setQuitEnabled(true); - } - }); - } - - /** - * Returns the {@link FileOption file options} for the currently selected - * file. - * - * @return The {@link FileOption}s for the selected file, or {@code null} if - * no file is selected - */ - private FileOption getSelectedFile() { - ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); - if (scannedFile == null) { - return null; - } - return project.getFileOption(scannedFile.getFilename()); - } - - // - // INTERFACE ActionListener - // - - /** - * {@inheritDoc} - */ - public void actionPerformed(ActionEvent actionEvent) { - Object source = actionEvent.getSource(); - if ((source instanceof JCheckBox) && ("ignore-hidden-files".equals(((JCheckBox) source).getName()))) { - project.setIgnoreHiddenFiles(((JCheckBox) source).isSelected()); - actionScan(); - return; - } - ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); - if (scannedFile == null) { - return; - } - String filename = scannedFile.getFilename(); - FileOption fileOption = project.getFileOption(filename); - if (source instanceof JCheckBox) { - JCheckBox checkBox = (JCheckBox) source; - if ("default-file".equals(checkBox.getName())) { - if (checkBox.isSelected()) { - if (filename.indexOf('/') > -1) { - JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.invalid-default-file"), null, JOptionPane.ERROR_MESSAGE); - checkBox.setSelected(false); - } else { - project.setIndexFile(filename); - } - } else { - if (filename.equals(project.getIndexFile())) { - project.setIndexFile(null); - } - } - } else if ("insert".equals(checkBox.getName())) { - boolean isInsert = checkBox.isSelected(); - fileOption.setInsert(isInsert); - fileOptionsInsertRedirectCheckBox.setEnabled(!isInsert); - } else if ("force-insert".equals(checkBox.getName())) { - boolean isForceInsert = checkBox.isSelected(); - fileOption.setForceInsert(isForceInsert); - } else if ("insert-redirect".equals(checkBox.getName())) { - boolean isInsertRedirect = checkBox.isSelected(); - fileOption.setInsertRedirect(isInsertRedirect); - fileOptionsCustomKeyTextField.setEnabled(isInsertRedirect); - } else if ("rename".equals(checkBox.getName())) { - boolean isRenamed = checkBox.isSelected(); - fileOptionsRenameTextField.setEnabled(isRenamed); - fileOption.setChangedName(isRenamed ? fileOptionsRenameTextField.getText() : ""); - } - } else if (source instanceof JComboBox) { - JComboBox comboBox = (JComboBox) source; - if ("project-files.mime-type".equals(comboBox.getName())) { - fileOption.setMimeType((String) comboBox.getSelectedItem()); - } - } - } - - // - // INTERFACE ListSelectionListener - // - - /** - * {@inheritDoc} - */ - @SuppressWarnings("null") - public void valueChanged(ListSelectionEvent e) { - ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); - boolean enabled = scannedFile != null; - String filename = (scannedFile == null) ? null : scannedFile.getFilename(); - defaultFileCheckBox.setEnabled(enabled); - fileOptionsInsertCheckBox.setEnabled(enabled); - fileOptionsRenameCheckBox.setEnabled(enabled); - fileOptionsMIMETypeComboBox.setEnabled(enabled); - if (filename != null) { - FileOption fileOption = project.getFileOption(filename); - defaultFileCheckBox.setSelected(filename.equals(project.getIndexFile())); - fileOptionsInsertCheckBox.setSelected(fileOption.isInsert()); - fileOptionsForceInsertCheckBox.setEnabled(scannedFile.getHash().equals(fileOption.getLastInsertHash())); - fileOptionsForceInsertCheckBox.setSelected(fileOption.isForceInsert()); - fileOptionsInsertRedirectCheckBox.setEnabled(!fileOption.isInsert()); - fileOptionsInsertRedirectCheckBox.setSelected(fileOption.isInsertRedirect()); - fileOptionsCustomKeyTextField.setEnabled(fileOption.isInsertRedirect()); - fileOptionsCustomKeyTextField.setText(fileOption.getCustomKey()); - fileOptionsRenameCheckBox.setSelected(fileOption.hasChangedName()); - fileOptionsRenameTextField.setEnabled(fileOption.hasChangedName()); - fileOptionsRenameTextField.setText(fileOption.getChangedName()); - fileOptionsMIMETypeComboBox.getModel().setSelectedItem(fileOption.getMimeType()); - } else { - defaultFileCheckBox.setSelected(false); - fileOptionsInsertCheckBox.setSelected(true); - fileOptionsForceInsertCheckBox.setEnabled(false); - fileOptionsForceInsertCheckBox.setSelected(false); - fileOptionsInsertRedirectCheckBox.setEnabled(false); - fileOptionsInsertRedirectCheckBox.setSelected(false); - fileOptionsCustomKeyTextField.setEnabled(false); - fileOptionsCustomKeyTextField.setText("CHK@"); - fileOptionsRenameCheckBox.setEnabled(false); - fileOptionsRenameCheckBox.setSelected(false); - fileOptionsRenameTextField.setEnabled(false); - fileOptionsRenameTextField.setText(""); - fileOptionsMIMETypeComboBox.getModel().setSelectedItem(DefaultMIMETypes.DEFAULT_MIME_TYPE); - } - } - - // - // INTERFACE DocumentListener - // - - /** - * Updates the options of the currently selected file with the changes made - * in the “custom key” textfield. - * - * @param documentEvent - * The document event to process - */ - private void processDocumentUpdate(DocumentEvent documentEvent) { - ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); - if (scannedFile == null) { - return; - } - FileOption fileOption = project.getFileOption(scannedFile.getFilename()); - Document document = documentEvent.getDocument(); - try { - String text = document.getText(0, document.getLength()); - fileOption.setCustomKey(text); - } catch (BadLocationException ble1) { - /* ignore. */ - } - } - - /** - * {@inheritDoc} - */ - public void changedUpdate(DocumentEvent documentEvent) { - processDocumentUpdate(documentEvent); - } - - /** - * {@inheritDoc} - */ - public void insertUpdate(DocumentEvent documentEvent) { - processDocumentUpdate(documentEvent); - } - - /** - * {@inheritDoc} - */ - public void removeUpdate(DocumentEvent documentEvent) { - processDocumentUpdate(documentEvent); - } - -} diff --git a/src/de/todesbaum/jsite/gui/ProjectInsertPage.java b/src/de/todesbaum/jsite/gui/ProjectInsertPage.java deleted file mode 100644 index bfd8b89..0000000 --- a/src/de/todesbaum/jsite/gui/ProjectInsertPage.java +++ /dev/null @@ -1,534 +0,0 @@ -/* - * jSite - ProjectInsertPage.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.awt.BorderLayout; -import java.awt.Font; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.ClipboardOwner; -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.text.DateFormat; -import java.text.MessageFormat; -import java.util.Date; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JTextField; -import javax.swing.SwingUtilities; - -import de.todesbaum.jsite.application.AbortedException; -import de.todesbaum.jsite.application.Freenet7Interface; -import de.todesbaum.jsite.application.InsertListener; -import de.todesbaum.jsite.application.Project; -import de.todesbaum.jsite.application.ProjectInserter; -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.jsite.i18n.I18nContainer; -import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; -import de.todesbaum.util.freenet.fcp2.PriorityClass; -import de.todesbaum.util.io.StreamCopier.ProgressListener; -import de.todesbaum.util.swing.TWizard; -import de.todesbaum.util.swing.TWizardPage; - -/** - * Wizard page that shows the progress of an insert. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class ProjectInsertPage extends TWizardPage implements InsertListener, ClipboardOwner { - - /** The logger. */ - private static final Logger logger = Logger.getLogger(ProjectInsertPage.class.getName()); - - /** The project inserter. */ - private ProjectInserter projectInserter; - - /** The “copy URI” action. */ - private Action copyURIAction; - - /** The “request URI” textfield. */ - private JTextField requestURITextField; - - /** The “start time” label. */ - private JLabel startTimeLabel; - - /** The progress bar. */ - private JProgressBar progressBar; - - /** The start time of the insert. */ - private long startTime = 0; - - /** The number of inserted blocks. */ - private volatile int insertedBlocks; - - /** Whether the “copy URI to clipboard” button was used. */ - private boolean uriCopied; - - /** Whether the insert is currently running. */ - private volatile boolean running = false; - - /** - * Creates a new progress insert wizard page. - * - * @param wizard - * The wizard this page belongs to - */ - public ProjectInsertPage(final TWizard wizard) { - super(wizard); - createActions(); - pageInit(); - setHeading(I18n.getMessage("jsite.insert.heading")); - setDescription(I18n.getMessage("jsite.insert.description")); - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - setHeading(I18n.getMessage("jsite.insert.heading")); - setDescription(I18n.getMessage("jsite.insert.description")); - } - }); - projectInserter = new ProjectInserter(); - projectInserter.addInsertListener(this); - } - - /** - * Creates all used actions. - */ - private void createActions() { - copyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionCopyURI(); - } - }; - copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); - copyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U); - copyURIAction.setEnabled(false); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - copyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri")); - copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); - } - }); - } - - /** - * Initializes the page. - */ - private void pageInit() { - setLayout(new BorderLayout(12, 12)); - add(createProjectInsertPanel(), BorderLayout.CENTER); - } - - /** - * Creates the main panel. - * - * @return The main panel - */ - private JComponent createProjectInsertPanel() { - JComponent projectInsertPanel = new JPanel(new GridBagLayout()); - - requestURITextField = new JTextField(); - requestURITextField.setEditable(false); - - startTimeLabel = new JLabel(); - - progressBar = new JProgressBar(0, 1); - progressBar.setStringPainted(true); - progressBar.setValue(0); - - final JLabel projectInformationLabel = new JLabel("" + I18n.getMessage("jsite.insert.project-information") + ""); - projectInsertPanel.add(projectInformationLabel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); - final JLabel requestURILabel = new JLabel(I18n.getMessage("jsite.insert.request-uri") + ":"); - projectInsertPanel.add(requestURILabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); - projectInsertPanel.add(requestURITextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - final JLabel startTimeLeftLabel = new JLabel(I18n.getMessage("jsite.insert.start-time") + ":"); - projectInsertPanel.add(startTimeLeftLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); - projectInsertPanel.add(startTimeLabel, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - final JLabel progressLabel = new JLabel(I18n.getMessage("jsite.insert.progress") + ":"); - projectInsertPanel.add(progressLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); - projectInsertPanel.add(progressBar, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - projectInsertPanel.add(new JButton(copyURIAction), new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0)); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - projectInformationLabel.setText("" + I18n.getMessage("jsite.insert.project-information") + ""); - requestURILabel.setText(I18n.getMessage("jsite.insert.request-uri") + ":"); - startTimeLeftLabel.setText(I18n.getMessage("jsite.insert.start-time") + ":"); - if (startTime != 0) { - startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date(startTime))); - } else { - startTimeLabel.setText(""); - } - progressLabel.setText(I18n.getMessage("jsite.insert.progress") + ":"); - } - }); - - return projectInsertPanel; - } - - /** - * {@inheritDoc} - */ - @Override - public void pageAdded(TWizard wizard) { - this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); - this.wizard.setPreviousEnabled(false); - this.wizard.setNextName(I18n.getMessage("jsite.general.cancel")); - this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); - } - - /** - * Starts the insert. - */ - public void startInsert() { - running = true; - copyURIAction.setEnabled(false); - progressBar.setValue(0); - progressBar.setString(I18n.getMessage("jsite.insert.starting")); - progressBar.setFont(progressBar.getFont().deriveFont(Font.PLAIN)); - projectInserter.start(new ProgressListener() { - - public void onProgress(final long copied, final long length) { - SwingUtilities.invokeLater(new Runnable() { - - /** - * {@inheritDoc} - */ - @SuppressWarnings("synthetic-access") - public void run() { - int divisor = 1; - while (((copied / divisor) > Integer.MAX_VALUE) || ((length / divisor) > Integer.MAX_VALUE)) { - divisor *= 10; - } - progressBar.setMaximum((int) (length / divisor)); - progressBar.setValue((int) (copied / divisor)); - progressBar.setString("Uploaded: " + copied + " / " + length); - } - }); - } - }); - } - - /** - * Stops the currently running insert. - */ - public void stopInsert() { - if (running) { - wizard.setNextEnabled(false); - projectInserter.stop(); - } - } - - /** - * Returns whether the insert is currently running. - * - * @return {@code true} if the insert is currently running, {@code false} - * otherwise - */ - public boolean isRunning() { - return running; - } - - /** - * Sets the project to insert. - * - * @param project - * The project to insert - */ - public void setProject(final Project project) { - projectInserter.setProject(project); - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - requestURITextField.setText(project.getFinalRequestURI(1)); - } - }); - } - - /** - * Sets the freenet interface to use. - * - * @param freenetInterface - * The freenet interface to use - */ - public void setFreenetInterface(Freenet7Interface freenetInterface) { - projectInserter.setFreenetInterface(freenetInterface); - } - - /** - * Sets the project inserter’s temp directory. - * - * @see ProjectInserter#setTempDirectory(String) - * @param tempDirectory - * The temp directory to use, or {@code null} to use the system - * default - */ - public void setTempDirectory(String tempDirectory) { - projectInserter.setTempDirectory(tempDirectory); - } - - /** - * Returns whether the “copy URI to clipboard” button was used. - * - * @return {@code true} if an URI was copied to clipboard, {@code false} - * otherwise - */ - public boolean wasUriCopied() { - return uriCopied; - } - - /** - * Sets whether to use the “early encode“ flag for the insert. - * - * @param useEarlyEncode - * {@code true} to set the “early encode” flag for the insert, - * {@code false} otherwise - */ - public void setUseEarlyEncode(boolean useEarlyEncode) { - projectInserter.setUseEarlyEncode(useEarlyEncode); - } - - /** - * Sets the insert priority. - * - * @param priority - * The insert priority - */ - public void setPriority(PriorityClass priority) { - projectInserter.setPriority(priority); - } - - /** - * Sets the manifest putter to use for the insert. - * - * @see ProjectInserter#setManifestPutter(ManifestPutter) - * @param manifestPutter - * The manifest putter - */ - public void setManifestPutter(ManifestPutter manifestPutter) { - projectInserter.setManifestPutter(manifestPutter); - } - - // - // INTERFACE InsertListener - // - - /** - * {@inheritDoc} - */ - public void projectInsertStarted(final Project project) { - - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date())); - } - }); - } - - /** - * {@inheritDoc} - */ - public void projectUploadFinished(Project project) { - startTime = System.currentTimeMillis(); - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - progressBar.setString(I18n.getMessage("jsite.insert.starting")); - progressBar.setValue(0); - } - }); - } - - /** - * {@inheritDoc} - */ - public void projectURIGenerated(Project project, final String uri) { - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - copyURIAction.setEnabled(true); - requestURITextField.setText(uri); - } - }); - logger.log(Level.FINEST, "Insert generated URI: " + uri); - int slash = uri.indexOf('/'); - slash = uri.indexOf('/', slash + 1); - int secondSlash = uri.indexOf('/', slash + 1); - if (secondSlash == -1) { - secondSlash = uri.length(); - } - String editionNumber = uri.substring(slash + 1, secondSlash); - logger.log(Level.FINEST, "Extracted edition number: " + editionNumber); - int edition = -1; - try { - edition = Integer.valueOf(editionNumber); - } catch (NumberFormatException nfe1) { - /* ignore. */ - } - logger.log(Level.FINEST, "Insert edition: " + edition + ", Project edition: " + project.getEdition()); - if ((edition != -1) && (edition == project.getEdition())) { - JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.reinserted-edition"), I18n.getMessage("jsite.insert.reinserted-edition.title"), JOptionPane.INFORMATION_MESSAGE); - } - } - - /** - * {@inheritDoc} - */ - public void projectInsertProgress(Project project, final int succeeded, final int failed, final int fatal, final int total, final boolean finalized) { - insertedBlocks = succeeded; - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - if (total == 0) { - return; - } - progressBar.setMaximum(total); - progressBar.setValue(succeeded + failed + fatal); - int progress = (succeeded + failed + fatal) * 100 / total; - StringBuilder progressString = new StringBuilder(); - progressString.append(progress).append("% ("); - progressString.append(succeeded + failed + fatal).append('/').append(total); - progressString.append(") ("); - progressString.append(getTransferRate()); - progressString.append(' ').append(I18n.getMessage("jsite.insert.k-per-s")).append(')'); - progressBar.setString(progressString.toString()); - if (finalized) { - progressBar.setFont(progressBar.getFont().deriveFont(Font.BOLD)); - } - } - }); - } - - /** - * {@inheritDoc} - */ - public void projectInsertFinished(Project project, boolean success, Throwable cause) { - running = false; - if (success) { - String copyURILabel = I18n.getMessage("jsite.insert.okay-copy-uri"); - int selectedValue = JOptionPane.showOptionDialog(this, I18n.getMessage("jsite.insert.inserted"), I18n.getMessage("jsite.insert.done.title"), 0, JOptionPane.INFORMATION_MESSAGE, null, new Object[] { I18n.getMessage("jsite.general.ok"), copyURILabel }, copyURILabel); - if (selectedValue == 1) { - actionCopyURI(); - } - } else { - if (cause == null) { - JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-failed"), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE); - } else { - if (cause instanceof AbortedException) { - JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-aborted"), I18n.getMessage("jsite.insert.insert-aborted.title"), JOptionPane.INFORMATION_MESSAGE); - } else { - JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.insert.insert-failed-with-cause"), cause.getMessage()), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE); - } - } - } - SwingUtilities.invokeLater(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - progressBar.setValue(progressBar.getMaximum()); - progressBar.setString(I18n.getMessage("jsite.insert.done") + " (" + getTransferRate() + " " + I18n.getMessage("jsite.insert.k-per-s") + ")"); - wizard.setNextName(I18n.getMessage("jsite.wizard.next")); - wizard.setNextEnabled(true); - wizard.setQuitEnabled(true); - } - }); - } - - // - // ACTIONS - // - - /** - * Copies the request URI of the project to the clipboard. - */ - private void actionCopyURI() { - uriCopied = true; - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(new StringSelection(requestURITextField.getText()), this); - } - - /** - * Formats the given number so that it always has the the given number of - * fractional digits. - * - * @param number - * The number to format - * @param digits - * The number of fractional digits - * @return The formatted number - */ - private String formatNumber(double number, int digits) { - int multiplier = (int) Math.pow(10, digits); - String formattedNumber = String.valueOf((int) (number * multiplier) / (double) multiplier); - if (formattedNumber.indexOf('.') == -1) { - formattedNumber += '.'; - for (int digit = 0; digit < digits; digit++) { - formattedNumber += "0"; - } - } - return formattedNumber; - } - - /** - * Returns the formatted transfer rate at this point. - * - * @return The formatted transfer rate - */ - private String getTransferRate() { - return formatNumber(insertedBlocks * 32.0 / ((System.currentTimeMillis() - startTime) / 1000), 1); - } - - // - // INTERFACE ClipboardOwner - // - - /** - * {@inheritDoc} - */ - public void lostOwnership(Clipboard clipboard, Transferable contents) { - /* ignore. */ - } - -} diff --git a/src/de/todesbaum/jsite/gui/ProjectPage.java b/src/de/todesbaum/jsite/gui/ProjectPage.java deleted file mode 100644 index 98ad847..0000000 --- a/src/de/todesbaum/jsite/gui/ProjectPage.java +++ /dev/null @@ -1,724 +0,0 @@ -/* - * jSite - ProjectPage.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.gui; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.awt.Toolkit; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.ClipboardOwner; -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.event.ActionEvent; -import java.awt.event.KeyEvent; -import java.io.IOException; -import java.text.MessageFormat; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; -import javax.swing.border.EmptyBorder; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.text.AbstractDocument; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.DocumentFilter; - -import de.todesbaum.jsite.application.Freenet7Interface; -import de.todesbaum.jsite.application.KeyDialog; -import de.todesbaum.jsite.application.Project; -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.jsite.i18n.I18nContainer; -import de.todesbaum.util.swing.SortedListModel; -import de.todesbaum.util.swing.TLabel; -import de.todesbaum.util.swing.TWizard; -import de.todesbaum.util.swing.TWizardPage; - -/** - * Wizard page that lets the user manage his projects and start inserts. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class ProjectPage extends TWizardPage implements ListSelectionListener, DocumentListener, ClipboardOwner { - - /** The freenet interface. */ - private Freenet7Interface freenetInterface; - - /** The “browse” action. */ - private Action projectLocalPathBrowseAction; - - /** The “add project” action. */ - private Action projectAddAction; - - /** The “delete project” action. */ - private Action projectDeleteAction; - - /** The “clone project” action. */ - private Action projectCloneAction; - - /** The “manage keys” action. */ - private Action projectManageKeysAction; - - /** The “copy URI” action. */ - private Action projectCopyURIAction; - - /** The “reset edition” action. */ - private Action projectResetEditionAction; - - /** The file chooser. */ - private JFileChooser pathChooser; - - /** The project list model. */ - private SortedListModel projectListModel; - - /** The project list scroll pane. */ - private JScrollPane projectScrollPane; - - /** The project list. */ - private JList projectList; - - /** The project name textfield. */ - private JTextField projectNameTextField; - - /** The project description textfield. */ - private JTextField projectDescriptionTextField; - - /** The local path textfield. */ - private JTextField projectLocalPathTextField; - - /** The textfield for the complete URI. */ - private JTextField projectCompleteUriTextField; - - /** The project path textfield. */ - private JTextField projectPathTextField; - - /** Whether the “copy URI to clipboard” action was used. */ - private boolean uriCopied; - - /** - * Creates a new project page. - * - * @param wizard - * The wizard this page belongs to - */ - public ProjectPage(final TWizard wizard) { - super(wizard); - setLayout(new BorderLayout(12, 12)); - dialogInit(); - setHeading(I18n.getMessage("jsite.project.heading")); - setDescription(I18n.getMessage("jsite.project.description")); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - setHeading(I18n.getMessage("jsite.project.heading")); - setDescription(I18n.getMessage("jsite.project.description")); - } - }); - } - - /** - * Initializes the page. - */ - private void dialogInit() { - createActions(); - - pathChooser = new JFileChooser(); - projectListModel = new SortedListModel(); - projectList = new JList(projectListModel); - projectList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - projectList.addListSelectionListener(this); - - add(projectScrollPane = new JScrollPane(projectList), BorderLayout.LINE_START); - projectScrollPane.setPreferredSize(new Dimension(150, projectList.getPreferredSize().height)); - add(createInformationPanel(), BorderLayout.CENTER); - } - - /** - * {@inheritDoc} - */ - @Override - public void pageAdded(TWizard wizard) { - super.pageAdded(wizard); - projectList.clearSelection(); - this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes")); - this.wizard.setNextName(I18n.getMessage("jsite.wizard.next")); - this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); - this.wizard.setNextEnabled(false); - } - - /** - * Adds the given listener to the list of listeners. - * - * @param listener - * The listener to add - */ - public void addListSelectionListener(ListSelectionListener listener) { - projectList.addListSelectionListener(listener); - } - - /** - * Removes the given listener from the list of listeners. - * - * @param listener - * The listener to remove - */ - public void removeListSelectionListener(ListSelectionListener listener) { - projectList.removeListSelectionListener(listener); - } - - /** - * Creates all actions. - */ - private void createActions() { - projectLocalPathBrowseAction = new AbstractAction(I18n.getMessage("jsite.project.action.browse")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionLocalPathBrowse(); - } - }; - projectLocalPathBrowseAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.browse.tooltip")); - projectLocalPathBrowseAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_B); - projectLocalPathBrowseAction.setEnabled(false); - - projectAddAction = new AbstractAction(I18n.getMessage("jsite.project.action.add-project")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionAdd(); - } - }; - projectAddAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.add-project.tooltip")); - projectAddAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_A); - - projectDeleteAction = new AbstractAction(I18n.getMessage("jsite.project.action.delete-project")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionDelete(); - } - }; - projectDeleteAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.delete-project.tooltip")); - projectDeleteAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_D); - projectDeleteAction.setEnabled(false); - - projectCloneAction = new AbstractAction(I18n.getMessage("jsite.project.action.clone-project")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionClone(); - } - }; - projectCloneAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.clone-project.tooltip")); - projectCloneAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_L); - projectCloneAction.setEnabled(false); - - projectCopyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionCopyURI(); - } - }; - projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); - projectCopyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U); - projectCopyURIAction.setEnabled(false); - - projectManageKeysAction = new AbstractAction(I18n.getMessage("jsite.project.action.manage-keys")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionManageKeys(); - } - }; - projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip")); - projectManageKeysAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_M); - projectManageKeysAction.setEnabled(false); - - projectResetEditionAction = new AbstractAction(I18n.getMessage("jsite.project.action.reset-edition")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - actionResetEdition(); - } - }; - projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip")); - projectResetEditionAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R); - projectResetEditionAction.setEnabled(false); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - projectLocalPathBrowseAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.browse")); - projectLocalPathBrowseAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.browse.tooltip")); - projectAddAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.add-project")); - projectAddAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.add-project.tooltip")); - projectDeleteAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.delete-project")); - projectDeleteAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.delete-project.tooltip")); - projectCloneAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.clone-project")); - projectCloneAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.clone-project.tooltip")); - projectCopyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri")); - projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); - projectManageKeysAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.manage-keys")); - projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip")); - projectResetEditionAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.reset-edition")); - projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip")); - pathChooser.setApproveButtonText(I18n.getMessage("jsite.project.action.browse.choose")); - } - }); - } - - /** - * Creates the information panel. - * - * @return The information panel - */ - private JComponent createInformationPanel() { - JPanel informationPanel = new JPanel(new BorderLayout(12, 12)); - - JPanel informationTable = new JPanel(new GridBagLayout()); - - JPanel functionButtons = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12)); - functionButtons.setBorder(new EmptyBorder(-12, -12, -12, -12)); - functionButtons.add(new JButton(projectAddAction)); - functionButtons.add(new JButton(projectDeleteAction)); - functionButtons.add(new JButton(projectCloneAction)); - functionButtons.add(new JButton(projectManageKeysAction)); - - informationPanel.add(functionButtons, BorderLayout.PAGE_START); - informationPanel.add(informationTable, BorderLayout.CENTER); - - final JLabel projectInformationLabel = new JLabel("" + I18n.getMessage("jsite.project.project.information") + ""); - informationTable.add(projectInformationLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - - projectNameTextField = new JTextField(); - projectNameTextField.getDocument().putProperty("name", "project.name"); - projectNameTextField.getDocument().addDocumentListener(this); - projectNameTextField.setEnabled(false); - - final TLabel projectNameLabel = new TLabel(I18n.getMessage("jsite.project.project.name") + ":", KeyEvent.VK_N, projectNameTextField); - informationTable.add(projectNameLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - informationTable.add(projectNameTextField, new GridBagConstraints(1, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - projectDescriptionTextField = new JTextField(); - projectDescriptionTextField.getDocument().putProperty("name", "project.description"); - projectDescriptionTextField.getDocument().addDocumentListener(this); - projectDescriptionTextField.setEnabled(false); - - final TLabel projectDescriptionLabel = new TLabel(I18n.getMessage("jsite.project.project.description") + ":", KeyEvent.VK_D, projectDescriptionTextField); - informationTable.add(projectDescriptionLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - informationTable.add(projectDescriptionTextField, new GridBagConstraints(1, 2, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - projectLocalPathTextField = new JTextField(); - projectLocalPathTextField.getDocument().putProperty("name", "project.localpath"); - projectLocalPathTextField.getDocument().addDocumentListener(this); - projectLocalPathTextField.setEnabled(false); - - final TLabel projectLocalPathLabel = new TLabel(I18n.getMessage("jsite.project.project.local-path") + ":", KeyEvent.VK_L, projectLocalPathTextField); - informationTable.add(projectLocalPathLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - informationTable.add(projectLocalPathTextField, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - informationTable.add(new JButton(projectLocalPathBrowseAction), new GridBagConstraints(2, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - final JLabel projectAddressLabel = new JLabel("" + I18n.getMessage("jsite.project.project.address") + ""); - informationTable.add(projectAddressLabel, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0)); - - projectPathTextField = new JTextField(); - projectPathTextField.getDocument().putProperty("name", "project.path"); - projectPathTextField.getDocument().addDocumentListener(this); - ((AbstractDocument) projectPathTextField.getDocument()).setDocumentFilter(new DocumentFilter() { - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("synthetic-access") - public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { - super.insertString(fb, offset, string.replaceAll("/", ""), attr); - updateCompleteURI(); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("synthetic-access") - public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { - super.replace(fb, offset, length, text.replaceAll("/", ""), attrs); - updateCompleteURI(); - } - - /** - * {@inheritDoc} - */ - @Override - @SuppressWarnings("synthetic-access") - public void remove(FilterBypass fb, int offset, int length) throws BadLocationException { - super.remove(fb, offset, length); - updateCompleteURI(); - } - }); - projectPathTextField.setEnabled(false); - - final TLabel projectPathLabel = new TLabel(I18n.getMessage("jsite.project.project.path") + ":", KeyEvent.VK_P, projectPathTextField); - informationTable.add(projectPathLabel, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - informationTable.add(projectPathTextField, new GridBagConstraints(1, 5, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - projectCompleteUriTextField = new JTextField(); - projectCompleteUriTextField.setEditable(false); - final TLabel projectUriLabel = new TLabel(I18n.getMessage("jsite.project.project.uri") + ":", KeyEvent.VK_U, projectCompleteUriTextField); - informationTable.add(projectUriLabel, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); - informationTable.add(projectCompleteUriTextField, new GridBagConstraints(1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - informationTable.add(new JButton(projectCopyURIAction), new GridBagConstraints(2, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - public void run() { - projectInformationLabel.setText("" + I18n.getMessage("jsite.project.project.information") + ""); - projectNameLabel.setText(I18n.getMessage("jsite.project.project.name") + ":"); - projectDescriptionLabel.setText(I18n.getMessage("jsite.project.project.description") + ":"); - projectLocalPathLabel.setText(I18n.getMessage("jsite.project.project.local-path") + ":"); - projectAddressLabel.setText("" + I18n.getMessage("jsite.project.project.address") + ""); - projectPathLabel.setText(I18n.getMessage("jsite.project.project.path") + ":"); - projectUriLabel.setText(I18n.getMessage("jsite.project.project.uri") + ":"); - } - }); - - return informationPanel; - } - - /** - * Sets the project list. - * - * @param projects - * The list of projects - */ - public void setProjects(Project[] projects) { - projectListModel.clear(); - for (Project project : projects) { - projectListModel.add(project); - } - } - - /** - * Returns the list of projects. - * - * @return The list of projects - */ - public Project[] getProjects() { - return projectListModel.toArray(new Project[projectListModel.size()]); - } - - /** - * Sets the freenet interface to use. - * - * @param freenetInterface - * The freenetInterface to use - */ - public void setFreenetInterface(Freenet7Interface freenetInterface) { - this.freenetInterface = freenetInterface; - } - - /** - * Returns the currently selected project. - * - * @return The currently selected project - */ - public Project getSelectedProject() { - return (Project) projectList.getSelectedValue(); - } - - /** - * Returns whether the “copy URI to clipboard” button was used. - * - * @return {@code true} if the “copy URI to clipboard” button was used, - * {@code false} otherwise - */ - public boolean wasUriCopied() { - return uriCopied; - } - - /** - * Updates the currently selected project with changed information from a - * textfield. - * - * @param documentEvent - * The document event to process - */ - private void setTextField(DocumentEvent documentEvent) { - Document document = documentEvent.getDocument(); - String propertyName = (String) document.getProperty("name"); - Project project = (Project) projectList.getSelectedValue(); - if (project == null) { - return; - } - try { - String text = document.getText(0, document.getLength()).trim(); - if ("project.name".equals(propertyName)) { - project.setName(text); - projectList.repaint(); - } else if ("project.description".equals(propertyName)) { - project.setDescription(text); - } else if ("project.localpath".equals(propertyName)) { - project.setLocalPath(text); - } else if ("project.privatekey".equals(propertyName)) { - project.setInsertURI(text); - } else if ("project.publickey".equals(propertyName)) { - project.setRequestURI(text); - } else if ("project.path".equals(propertyName)) { - project.setPath(text); - } - } catch (BadLocationException e) { - /* ignore. */ - } - } - - // - // ACTIONS - // - - /** - * Lets the user choose a local path for a project. - */ - private void actionLocalPathBrowse() { - Project project = (Project) projectList.getSelectedValue(); - if (project == null) { - return; - } - pathChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - if (pathChooser.showDialog(this, I18n.getMessage("jsite.project.action.browse.choose")) == JFileChooser.APPROVE_OPTION) { - projectLocalPathTextField.setText(pathChooser.getSelectedFile().getPath()); - } - } - - /** - * Adds a new project. - */ - private void actionAdd() { - String[] keyPair = null; - if (!freenetInterface.hasNode()) { - JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.project-files.no-node-selected"), null, JOptionPane.ERROR_MESSAGE); - return; - } - try { - keyPair = freenetInterface.generateKeyPair(); - } catch (IOException ioe1) { - JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.keygen.io-error"), ioe1.getMessage()), null, JOptionPane.ERROR_MESSAGE); - return; - } - Project newProject = new Project(); - newProject.setName(I18n.getMessage("jsite.project.new-project.name")); - newProject.setInsertURI(keyPair[0]); - newProject.setRequestURI(keyPair[1]); - newProject.setEdition(-1); - newProject.setPath(""); - projectListModel.add(newProject); - projectScrollPane.revalidate(); - projectScrollPane.repaint(); - projectList.setSelectedIndex(projectListModel.indexOf(newProject)); - } - - /** - * Deletes the currently selected project. - */ - private void actionDelete() { - int selectedIndex = projectList.getSelectedIndex(); - if (selectedIndex > -1) { - if (JOptionPane.showConfirmDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.action.delete-project.confirm"), ((Project) projectList.getSelectedValue()).getName()), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { - projectListModel.remove(selectedIndex); - projectList.clearSelection(); - if (projectListModel.getSize() != 0) { - projectList.setSelectedIndex(Math.min(selectedIndex, projectListModel.getSize() - 1)); - } - } - } - } - - /** - * Clones the currently selected project. - */ - private void actionClone() { - int selectedIndex = projectList.getSelectedIndex(); - if (selectedIndex > -1) { - Project newProject = new Project((Project) projectList.getSelectedValue()); - newProject.setName(MessageFormat.format(I18n.getMessage("jsite.project.action.clone-project.copy"), newProject.getName())); - projectListModel.add(newProject); - projectList.setSelectedIndex(projectListModel.indexOf(newProject)); - } - } - - /** - * Copies the request URI of the currently selected project to the - * clipboard. - */ - private void actionCopyURI() { - int selectedIndex = projectList.getSelectedIndex(); - if (selectedIndex > -1) { - Project selectedProject = (Project) projectList.getSelectedValue(); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - clipboard.setContents(new StringSelection(selectedProject.getFinalRequestURI(0)), this); - uriCopied = true; - } - } - - /** - * Opens a {@link KeyDialog} and lets the user manipulate the keys of the - * project. - */ - private void actionManageKeys() { - int selectedIndex = projectList.getSelectedIndex(); - if (selectedIndex > -1) { - Project selectedProject = (Project) projectList.getSelectedValue(); - KeyDialog keyDialog = new KeyDialog(freenetInterface, wizard); - keyDialog.setPrivateKey(selectedProject.getInsertURI()); - keyDialog.setPublicKey(selectedProject.getRequestURI()); - keyDialog.setVisible(true); - if (!keyDialog.wasCancelled()) { - String originalPublicKey = selectedProject.getRequestURI(); - String originalPrivateKey = selectedProject.getInsertURI(); - selectedProject.setInsertURI(keyDialog.getPrivateKey()); - selectedProject.setRequestURI(keyDialog.getPublicKey()); - if (!originalPublicKey.equals(selectedProject.getRequestURI()) || !originalPrivateKey.equals(selectedProject.getInsertURI())) { - selectedProject.setEdition(-1); - } - updateCompleteURI(); - } - } - } - - /** - * Resets the edition of the currently selected project. - */ - private void actionResetEdition() { - if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.reset-edition"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { - return; - } - int selectedIndex = projectList.getSelectedIndex(); - if (selectedIndex > -1) { - Project selectedProject = (Project) projectList.getSelectedValue(); - selectedProject.setEdition(-1); - updateCompleteURI(); - } - } - - /** - * Updates the complete URI text field. - */ - private void updateCompleteURI() { - int selectedIndex = projectList.getSelectedIndex(); - if (selectedIndex > -1) { - Project selectedProject = (Project) projectList.getSelectedValue(); - projectCompleteUriTextField.setText(selectedProject.getFinalRequestURI(0)); - } - } - - // - // INTERFACE ListSelectionListener - // - - /** - * {@inheritDoc} - */ - public void valueChanged(ListSelectionEvent listSelectionEvent) { - int selectedRow = projectList.getSelectedIndex(); - Project selectedProject = (Project) projectList.getSelectedValue(); - projectNameTextField.setEnabled(selectedRow > -1); - projectDescriptionTextField.setEnabled(selectedRow > -1); - projectLocalPathTextField.setEnabled(selectedRow > -1); - projectPathTextField.setEnabled(selectedRow > -1); - projectLocalPathBrowseAction.setEnabled(selectedRow > -1); - projectDeleteAction.setEnabled(selectedRow > -1); - projectCloneAction.setEnabled(selectedRow > -1); - projectCopyURIAction.setEnabled(selectedRow > -1); - projectManageKeysAction.setEnabled(selectedRow > -1); - projectResetEditionAction.setEnabled(selectedRow > -1); - if (selectedRow > -1) { - projectNameTextField.setText(selectedProject.getName()); - projectDescriptionTextField.setText(selectedProject.getDescription()); - projectLocalPathTextField.setText(selectedProject.getLocalPath()); - projectPathTextField.setText(selectedProject.getPath()); - projectCompleteUriTextField.setText("freenet:" + selectedProject.getFinalRequestURI(0)); - } else { - projectNameTextField.setText(""); - projectDescriptionTextField.setText(""); - projectLocalPathTextField.setText(""); - projectPathTextField.setText(""); - projectCompleteUriTextField.setText(""); - } - } - - // - // INTERFACE ChangeListener - // - - // - // INTERFACE DocumentListener - // - - /** - * {@inheritDoc} - */ - public void insertUpdate(DocumentEvent documentEvent) { - setTextField(documentEvent); - } - - /** - * {@inheritDoc} - */ - public void removeUpdate(DocumentEvent documentEvent) { - setTextField(documentEvent); - } - - /** - * {@inheritDoc} - */ - public void changedUpdate(DocumentEvent documentEvent) { - setTextField(documentEvent); - } - - // - // INTERFACE ClipboardOwner - // - - /** - * {@inheritDoc} - */ - public void lostOwnership(Clipboard clipboard, Transferable contents) { - /* ignore. */ - } - -} diff --git a/src/de/todesbaum/jsite/i18n/I18n.java b/src/de/todesbaum/jsite/i18n/I18n.java deleted file mode 100644 index 4c1cceb..0000000 --- a/src/de/todesbaum/jsite/i18n/I18n.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * jSite - I18n.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.i18n; - -import java.util.Locale; -import java.util.MissingResourceException; -import java.util.ResourceBundle; - -/** - * Maps i18n keys to translated texts, depending on a current locale. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class I18n { - - /** The default locale, English. */ - private static Locale defaultLocale = new Locale("en"); - - /** The current locale. */ - private static Locale currentLocale; - - /** - * Returns the currently set locale. - * - * @return The current locale - */ - public static Locale getLocale() { - if (currentLocale == null) { - currentLocale = Locale.getDefault(); - } - return currentLocale; - } - - /** - * Sets the current locale. - * - * @param locale - * The new current locale - */ - public static void setLocale(Locale locale) { - currentLocale = locale; - Locale.setDefault(locale); - } - - /** - * Returns the resource bundle for the current locale. - * - * @return The resource bundle for the current locale - */ - public static ResourceBundle getResourceBundle() { - return getResourceBundle(getLocale()); - } - - /** - * Returns the resource bundle for the given locale. - * - * @param locale - * The locale to get the resource bundle for - * @return The resource bundle for the given locale - */ - public static ResourceBundle getResourceBundle(Locale locale) { - return ResourceBundle.getBundle("de.todesbaum.jsite.i18n.jSite", locale); - } - - /** - * Retrieves a translated text for the given i18n key. If the resource - * bundle for the current locale does not have a translation for the given - * key, the default locale is tried. If that fails, the key is returned. - * - * @param key - * The key to get the translation for - * @return The translated value, or the key itself if not translation can be - * found - */ - public static String getMessage(String key) { - try { - return getResourceBundle().getString(key); - } catch (MissingResourceException mre1) { - try { - return getResourceBundle(defaultLocale).getString(key); - } catch (MissingResourceException mre2) { - return key; - } - } - } - -} diff --git a/src/de/todesbaum/jsite/i18n/I18nContainer.java b/src/de/todesbaum/jsite/i18n/I18nContainer.java deleted file mode 100644 index da2e0c3..0000000 --- a/src/de/todesbaum/jsite/i18n/I18nContainer.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * jSite - I18nContainer.java - Copyright © 2007–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.i18n; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * Container that collects {@link Runnable}s that change the texts of GUI - * components when the current locale has changed. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class I18nContainer implements Iterable { - - /** The container singleton. */ - private static final I18nContainer singleton = new I18nContainer(); - - /** The list of runnables that change texts. */ - private final List i18nRunnables = Collections.synchronizedList(new ArrayList()); - - /** - * The list of runnables that change texts and run after - * {@link #i18nRunnables}. - */ - private final List i18nPostRunnables = Collections.synchronizedList(new ArrayList()); - - /** - * Returns the singleton instance. - * - * @return The singleton instance - */ - public static I18nContainer getInstance() { - return singleton; - } - - /** - * Registers an i18n runnable that is run when the current locale has - * changed. - * - * @param i18nRunnable - * The runnable to register - */ - public void registerRunnable(Runnable i18nRunnable) { - i18nRunnables.add(i18nRunnable); - } - - /** - * Registers a {@link Runnable} that changes texts when the current locale - * has changed and runs after {@link #i18nRunnables} have run. - * - * @param i18nPostRunnable - * The runnable to register - */ - public void registerPostRunnable(Runnable i18nPostRunnable) { - i18nPostRunnables.add(i18nPostRunnable); - } - - /** - * {@inheritDoc} - *

- * Returns a combined list of {@link #i18nRunnables} and - * {@link #i18nPostRunnables}, in that order. - */ - public Iterator iterator() { - List allRunnables = new ArrayList(); - allRunnables.addAll(i18nRunnables); - allRunnables.addAll(i18nPostRunnables); - return allRunnables.iterator(); - } - -} diff --git a/src/de/todesbaum/jsite/i18n/jSite.properties b/src/de/todesbaum/jsite/i18n/jSite.properties deleted file mode 100644 index cd80c67..0000000 --- a/src/de/todesbaum/jsite/i18n/jSite.properties +++ /dev/null @@ -1,196 +0,0 @@ -# -# jSite - jSite.properties - Copyright © 2006–2012 David Roden -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# - -# English language file by David Roden - -# Attention, translators! Most of the strings here are used directly. -# However, some of them are parsed by MessageFormat -# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html) -# and thus have to adhere to some rules (check the URL above). This is the -# case when a line contains placeholders like {0} or {0,number}! In these -# lines single quotes (ASCII 39) needs to be escaped by entering them twice, -# otherwise the placeholder will not be replaced! - -jsite.general.ok=OK -jsite.general.cancel=Cancel - -jsite.wizard.previous=Previous -jsite.wizard.next=Next -jsite.wizard.quit=Quit - -jsite.quit.question=Do you really want to quit? -jsite.quit.question.title=Really quit? -jsite.quit.overwrite-configuration=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 -jsite.menu.language.en=English -jsite.menu.language.de=Deutsch -jsite.menu.language.fr=Fran\u00e7ais -jsite.menu.language.it=Italiano -jsite.menu.language.pl=Polski -jsite.menu.nodes=Nodes -jsite.menu.nodes.manage-nodes=Manage nodes -jsite.menu.options=Options -jsite.menu.options.preferences=Preferences -jsite.menu.help=Help -jsite.menu.help.check-for-updates=Check for Updates -jsite.menu.help.about=About - -jsite.about.message=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. -jsite.node-manager.node-information=Node Information -jsite.node-manager.add-node=Add Node -jsite.node-manager.new-node=New Node -jsite.node-manager.delete-node=Delete Node -jsite.node-manager.delete-node.warning=Confirm node deletion

Really delete this node? -jsite.node-manager.name=Name -jsite.node-manager.hostname=Hostname -jsite.node-manager.port=Port - -jsite.preferences.heading=Preferences -jsite.preferences.description=Use this page to manage some global settings. -jsite.preferences.temp-directory=Directory for temporary files -jsite.preferences.temp-directory.default=Default (chosen by system) -jsite.preferences.temp-directory.custom=Custom -jsite.preferences.temp-directory.choose=Choose -jsite.preferences.temp-directory.choose.approve=Choose -jsite.preferences.config-directory=Location of configuration file -jsite.preferences.config-directory.jar=Next to the JAR file -jsite.preferences.config-directory.home=Home directory -jsite.preferences.config-directory.custom=Custom directory -jsite.preferences.insert-options=Insert options -jsite.preferences.insert-options.use-early-encode=Generate final URI early -jsite.preferences.insert-options.priority=Priority -jsite.preferences.insert-options.manifest-putter=Manifest Putter - -jsite.insert.heading=Project insert -jsite.insert.description=Please wait while the project is being inserted. -jsite.insert.project-information=Project information -jsite.insert.request-uri=Freesite -jsite.insert.start-time=Start time -jsite.insert.starting=Starting\u2026 -jsite.insert.done=Done. -jsite.insert.done.title=Insert done -jsite.insert.insert-aborted=The insert was aborted. -jsite.insert.insert-aborted.title=Insert Aborted -jsite.insert.progress=Progress -jsite.insert.k-per-s=KB/s -jsite.insert.insert-failed=Insert failed

The insert of the project failed.
Some files could not be inserted. -jsite.insert.insert-failed-with-cause=Insert failed

The insert of the project failed.
Some files could not be inserted.
The following error occured:

{0} -jsite.insert.insert-failed.title=Insert Failed -jsite.insert.inserted=Project inserted

Your project was inserted successfully. -jsite.insert.okay-copy-uri=Copy URI to Clipboard -jsite.insert.reinserted-edition=Edition Reinserted

The edition you are just inserting
has already been inserted before. -jsite.insert.reinserted-edition.title=Edition Reinserted - -jsite.file-scanner.can-not-read-directory=Can not read directory - -jsite.project.heading=Select a Project -jsite.project.description=Select a project to process from the list below, or create a new project. -jsite.project.action.browse=Browse -jsite.project.action.browse.choose=Choose -jsite.project.action.browse.tooltip=Browse for directory -jsite.project.action.add-project=Add project -jsite.project.action.add-project.tooltip=Add a new project -jsite.project.new-project.name=New Project -jsite.project.action.delete-project=Delete project -jsite.project.action.delete-project.tooltip=Delete a project -jsite.project.action.delete-project.confirm=Confirm deletion

The project \u201c{0}\u201d will be deleted!
Do you want to continue? -jsite.project.action.clone-project=Clone project -jsite.project.action.clone-project.copy=Copy of {0} -jsite.project.action.clone-project.tooltip=Clone the selected project -jsite.project.action.copy-uri=Copy URI to Clipboard -jsite.project.action.copy-uri.tooltip=Copies the URI of the project to the clipboard -jsite.project.action.manage-keys=Manage Keys -jsite.project.action.manage-keys.tooltip=Manages the keys of this project -jsite.project.action.reset-edition=Reset Edition -jsite.project.action.reset-edition.tooltip=Resets the edition number of the project -jsite.project.project.information=Project Information -jsite.project.project.name=Name -jsite.project.project.description=Description -jsite.project.project.local-path=Local path -jsite.project.project.address=Address -jsite.project.project.path=Freesite Path -jsite.project.project.edition=Edition -jsite.project.project.uri=URI -jsite.project.keygen.io-error=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!
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! - -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.ignore-hidden-files=Ignore hidden files -jsite.project-files.ignore-hidden-files.tooltip=When selected, hidden files are not inserted -jsite.project-files.file-options=File Options -jsite.project-files.default=Default file -jsite.project-files.default.tooltip=Specify that this file is the project\u2019s index file -jsite.project-files.insert=Insert -jsite.project-files.insert.tooltip=Uncheck if you do not want to insert this file -jsite.project-files.force-insert=Force insert -jsite.project-files.force-insert.tooltip=Forces the insert of this file even it is not modified -jsite.project-files.insert-redirect=Redirect -jsite.project-files.insert-redirect.tooltip=Check if you want to insert a redirect for this file -jsite.project-files.custom-key=Custom key -jsite.project-files.custom-key.tooltip=The externally created key for the file -jsite.project-files.rename=Rename -jsite.project-files.rename.tooltip=Renames the file in the uploaded site -jsite.project-files.mime-type=MIME type -jsite.project-files.mime-type.tooltip=Select the correct MIME type here if the detection failed -jsite.project-files.container=Container -jsite.project-files.container.tooltip=Selects a container for the current file -jsite.project-files.scan-error=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}) -jsite.update-checker.latest-version.title=Update Check -jsite.update-checker.latest-version.newer.message=You are running version {0} but a newer
version ({1}) has been found! -jsite.update-checker.latest-version.older.message=You are running version {0} but the
latest version seems to be {1}. -jsite.update-checker.latest-version.okay.message=You are currently running version {0}
which is the latest version. - -jsite.key-dialog.title=Manage Project Keys -jsite.key-dialog.button.ok.tooltip=Accepts the changes -jsite.key-dialog.button.cancel.tooltip=Discards the changes -jsite.key-dialog.button.generate=Regenerate Keys -jsite.key-dialog.button.generate.tooltip=Create a new key pair -jsite.key-dialog.label.keys=Keys -jsite.key-dialog.label.private-key=Private Key -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.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.warning.site-larger-than-2-mib=Site is larger than 2 MiB!

Your site contains more than 2 megabytes of data.
Due to bugs in Freenet it will probably not load correctly.
Try to reduce the size of your site, or continue at your own peril. - -jsite.error.no-node-selected=No node selected

Please select a node from the menu! -jsite.error.no-node-running=Node is not running

You can not insert a project if your node is not running.
Please start your node and try again. -jsite.error.no-local-path=No local path

You did not specify a local path for the files to insert.
It is not possible to continue without one. -jsite.error.no-path=No freesite path

You did not specify a freesite path.
It is not possible to continue without one. -jsite.error.index-missing=Your default file is missing

A default file was previously specified but it
does not exist anymore! Please select
a new default file in the list of files. -jsite.error.index-not-inserted=Default file not inserted

You have chosen not to insert the default file!
You need to either choose to insert it or select
a different default file! -jsite.error.no-custom-key=No custom key for file

You specified not to insert {0}
but failed to enter a key to redirect to! -jsite.error.no-files-to-insert=No files to insert

You do not have any files selected for insertion!
Please select at least one file to insert. -jsite.error.duplicate-file=Duplicate file

The file {0} is inserted twice!
Please check your filenames and redirects. diff --git a/src/de/todesbaum/jsite/i18n/jSite_de.properties b/src/de/todesbaum/jsite/i18n/jSite_de.properties deleted file mode 100644 index 8641d14..0000000 --- a/src/de/todesbaum/jsite/i18n/jSite_de.properties +++ /dev/null @@ -1,196 +0,0 @@ -# -# jSite - jSite_de.properties - Copyright © 2006–2012 David Roden -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# - -# German language file by David Roden - -# Attention, translators! Most of the strings here are used directly. -# However, some of them are parsed by MessageFormat -# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html) -# and thus have to adhere to some rules (check the URL above). This is the -# case when a line contains placeholders like {0} or {0,number}! In these -# lines single quotes (ASCII 39) needs to be escaped by entering them twice, -# otherwise the placeholder will not be replaced! - -jsite.general.ok=OK -jsite.general.cancel=Abbrechen - -jsite.wizard.previous=Zur\u00fcck -jsite.wizard.next=Vorw\u00e4rts -jsite.wizard.quit=Beenden - -jsite.quit.question=M\u00f6chten Sie jSite wirklich beenden? -jsite.quit.question.title=Wirklich beenden? -jsite.quit.overwrite-configuration=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 -jsite.menu.language.en=English -jsite.menu.language.de=Deutsch -jsite.menu.language.fr=Fran\u00e7ais -jsite.menu.language.it=Italiano -jsite.menu.language.pl=Polski -jsite.menu.nodes=Nodes -jsite.menu.nodes.manage-nodes=Nodes verwalten -jsite.menu.options=Optionen -jsite.menu.options.preferences=Einstellungen -jsite.menu.help=Hilfe -jsite.menu.help.check-for-updates=Auf Updates pr\u00fcfen -jsite.menu.help.about=\u00dcber - -jsite.about.message=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. -jsite.node-manager.node-information=Nodeinformation -jsite.node-manager.add-node=Node hinzuf\u00fcgen -jsite.node-manager.new-node=Neuer Node -jsite.node-manager.delete-node=Node l\u00f6schen -jsite.node-manager.delete-node.warning=Nodel\u00f6schung best\u00e4tigen

Wollen Sie diesen Node wirklich l\u00f6schen? -jsite.node-manager.name=Name -jsite.node-manager.hostname=Hostname -jsite.node-manager.port=Port - -jsite.preferences.heading=Einstellungen -jsite.preferences.description=Auf dieser Seite k\u00f6nnen einige globale Einstellungen vorgenommen werden. -jsite.preferences.temp-directory=Verzeichnis f\u00fcr tempor\u00e4re Dateien -jsite.preferences.temp-directory.default=Standard (vom System bestimmt) -jsite.preferences.temp-directory.custom=Eigenes -jsite.preferences.temp-directory.choose=Ausw\u00e4hlen -jsite.preferences.temp-directory.choose.approve=Ausw\u00e4hlen -jsite.preferences.config-directory=Lage der Konfigurationsdatei -jsite.preferences.config-directory.jar=Neben der JAR-Datei -jsite.preferences.config-directory.home=Benutzerverzeichnis -jsite.preferences.config-directory.custom=Angegebenes Verzeichnis -jsite.preferences.insert-options=Einf\u00fcgeoptionen -jsite.preferences.insert-options.use-early-encode=Endg\u00fcltige URI fr\u00fcher berechnen -jsite.preferences.insert-options.priority=Priorit\u00e4t -jsite.preferences.insert-options.manifest-putter=Manifesterstellung - -jsite.insert.heading=Projekt einf\u00fcgen -jsite.insert.description=Bitte warten Sie, w\u00e4hrend das Projekt eingef\u00fcgt wird. -jsite.insert.project-information=Projektinformationen -jsite.insert.request-uri=Freesite -jsite.insert.start-time=Beginn -jsite.insert.starting=Beginne\u2026 -jsite.insert.done=Fertig. -jsite.insert.done.title=Einf\u00fcgen abgeschlossen -jsite.insert.insert-aborted=Das Einf\u00fcgen wurde abgebrochen. -jsite.insert.insert-aborted.title=Einf\u00fcgen abgebrochen -jsite.insert.progress=Fortschritt -jsite.insert.k-per-s=KB/s -jsite.insert.insert-failed=Einf\u00fcgen fehlgeschlagen

Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da
einige Dateien nicht eingef\u00fcgt werden konnten. -jsite.insert.insert-failed-with-cause=Einf\u00fcgen fehlgeschlagen

Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da
einige Dateien nicht eingef\u00fcgt werden konnten.
Folgender Fehler trat auf:

{0} -jsite.insert.insert-failed.title=Einf\u00fcgen fehlgeschlagen -jsite.insert.inserted=Projekt eingef\u00fcgt

Ihr Projekt wurde erfolgreich eingef\u00fcgt. -jsite.insert.okay-copy-uri=URI kopieren -jsite.insert.reinserted-edition=Edition bereits eingef\u00fcgt

Die Edition, die gerade eingef\u00fcgt wird,
ist schon einmal eingef\u00fcgt worden. -jsite.insert.reinserted-edition.title=Edition bereits eingef\u00fcgt - -jsite.file-scanner.can-not-read-directory=Kann Verzeichnis nicht lesen - -jsite.project.heading=Projekt ausw\u00e4hlen -jsite.project.description=W\u00e4hlen Sie das Projekt aus, welches sie einf\u00fcgen m\u00f6chten, oder erstellen Sie ein neues Projekt. -jsite.project.action.browse=Durchsuchen -jsite.project.action.browse.choose=Ausw\u00e4hlen -jsite.project.action.browse.tooltip=Lokalen Pfad f\u00fcr Projekt ausw\u00e4hlen -jsite.project.action.add-project=Projekt erstellen -jsite.project.action.add-project.tooltip=Ein neues Projekt erstellen -jsite.project.new-project.name=Neues Projekt -jsite.project.action.delete-project=Projekt l\u00f6schen -jsite.project.action.delete-project.tooltip=Ein Projekt l\u00f6schen -jsite.project.action.delete-project.confirm=L\u00f6schung best\u00e4tigen

Das Projekt \u201e{0}\u201c wird gel\u00f6scht!
M\u00f6chten Sie fortfahren? -jsite.project.action.clone-project=Projekt duplizieren -jsite.project.action.clone-project.copy=Kopie von {0} -jsite.project.action.clone-project.tooltip=Das ausgew\u00e4hlte Projekt duplizieren -jsite.project.action.copy-uri=URI kopieren -jsite.project.action.copy-uri.tooltip=Kopiert die URI des ausgew\u00e4hlten Projektes in die Zwischenablage -jsite.project.action.manage-keys=Schl\u00fcsselverwaltung -jsite.project.action.manage-keys.tooltip=Verwaltet die Schl\u00fcssel des Projekts -jsite.project.action.reset-edition=Edition zur\u00fccksetzen -jsite.project.action.reset-edition.tooltip=Setzt die Editionsnummer des Projekts zur\u00fcck -jsite.project.project.information=Projektinformation -jsite.project.project.name=Name -jsite.project.project.description=Beschreibung -jsite.project.project.local-path=Lokaler Pfad -jsite.project.project.address=Adresse -jsite.project.project.path=Seitenpfad -jsite.project.project.edition=Edition -jsite.project.project.uri=Anfrage-URI -jsite.project.keygen.io-error=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! 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! - -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.ignore-hidden-files=Versteckte Dateien ignorieren -jsite.project-files.ignore-hidden-files.tooltip=Verhindert, dass versteckte Dateien hochgeladen werden -jsite.project-files.file-options=Dateioptionen -jsite.project-files.default=Index-Datei -jsite.project-files.default.tooltip=Lege Index-Datei f\u00fcr Projekt fest -jsite.project-files.insert=Einf\u00fcgen -jsite.project-files.insert.tooltip=jSite f\u00fcgt diese Datei ein -jsite.project-files.force-insert=Einf\u00fcgen erzwingen -jsite.project-files.force-insert.tooltip=F\u00fcgt diese Datei ein, auch wenn sie nicht modifiziert wurde -jsite.project-files.insert-redirect=Umleitung -jsite.project-files.insert-redirect.tooltip=F\u00fcgt eine Umleitung ein -jsite.project-files.custom-key=Extern erstellter Schl\u00fcssel -jsite.project-files.custom-key.tooltip=Der extern erstellte Schl\u00fcssel f\u00fcr diese Datei -jsite.project-files.rename=Umbenennen -jsite.project-files.rename.tooltip=Benennt die Datei in der eingef\u00fcgten Seite um -jsite.project-files.mime-type=MIME-Typ -jsite.project-files.mime-type.tooltip=Den richtigen MIME-Typ hier ausw\u00e4hlen, wenn die automatische Erkennenung falsch ist -jsite.project-files.container=Container -jsite.project-files.container.tooltip=W\u00e4hlt einen Container f\u00fcr diese Datei aus -jsite.project-files.scan-error=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}) -jsite.update-checker.latest-version.title=Update\u00fcberpr\u00fcfung -jsite.update-checker.latest-version.newer.message=Es l\u00e4uft momentan Version {0}, aber eine
neue Version ({1}) wurde bereits gefunden! -jsite.update-checker.latest-version.older.message=Es l\u00e4uft momentan Version {0}, aber die
aktuelle Version ist erst {1}. -jsite.update-checker.latest-version.okay.message=Es l\u00e4uft momentan Version {0},
und diese Version ist aktuell. - -jsite.key-dialog.title=Projektschl\u00fcsselverwaltung -jsite.key-dialog.button.ok.tooltip=\u00c4nderungen akzeptieren -jsite.key-dialog.button.cancel.tooltip=\u00c4nderungen verwerfen -jsite.key-dialog.button.generate=Schl\u00fcssel neu generieren -jsite.key-dialog.button.generate.tooltip=Generiert ein neues Schl\u00fcsselpaar -jsite.key-dialog.label.keys=Schl\u00fcssel -jsite.key-dialog.label.private-key=Privater Schl\u00fcssel -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.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.warning.site-larger-than-2-mib=Ihr Projekt ist gr\u00f6\u00dfer als 2 Megabyte!

Ihr Projekt enth\u00e4lt mehr als 2 Megabyte an Daten. Aufgrund
eines Fehlers in Freenet wird die Seite wahrscheinlich nicht
korrekt angezeigt werden. Bitte reduzieren Sie die Gr\u00f6\u00dfe
Ihres Projektes, oder fahren Sie auf eigene Gefahr fort. - -jsite.error.no-node-selected=Kein Node ausgew\u00e4hlt

Bitte w\u00e4hlen Sie einen Node aus dem Men\u00fc! -jsite.error.no-node-running=Der Node l\u00e4uft nicht

Sie k\u00f6nnen das Projekt nicht einf\u00fcgen, wenn
Ihr Node nicht l\u00e4uft. Bitte starten Sie Ihren Node
und probieren Sie es erneut. -jsite.error.no-local-path=Kein lokaler Pfad

Sie haben keinen lokalen Pfad f\u00fcr die einzuf\u00fcgenden Dateien angegeben.
Es ist nicht m\u00f6glich, ohne lokalen Pfad weiter zu machen. -jsite.error.no-path=Kein Freesite-Pfad

Sie haben keinen Pfad f\u00fcr die Freesite angegeben.
Es ist nicht m\u00f6glich, ohne einen Freesite-Pfad weiter zu machen. -jsite.error.index-missing=Index-Datei fehlt!

Sie haben eine Index-Datei f\u00fcr das Project gew\u00e4hlt,
aber diese Index-Datei existiert nicht mehr!
Bitte w\u00e4hlen Sie eine neue Index-Datei. -jsite.error.index-not-inserted=Index-Datei nicht eingef\u00fcgt

Die index-Datei ist nicht zum Einf\u00fcgen ausgew\u00e4hlt!
Sie m\u00fcssen entweder w\u00e4hlen, die Index-Datei einzuf\u00fcgen,
oder Sie m\u00fcssen eine andere Index-Datei ausw\u00e4hlen! -jsite.error.no-custom-key=Kein externer Schl\u00fcssel

Sie haben angegeben, dass die Datei {0}
nicht eingef\u00fcgt werden soll. Allerdings haben Sie
keinen extern erstellten Schl\u00fcssel angegeben. -jsite.error.no-files-to-insert=Keine Dateien einzuf\u00fcgen

Es sind keine Dateien zum Einf\u00fcgen ausgew\u00e4hlt! Bitte
w\u00e4hlen Sie mindestens eine Datei zum Einf\u00fcgen aus! -jsite.error.duplicate-file=Doppelte Datei

Die Datei {0} wird zweimal eingef\u00fcgt!
Bitte \u00fcberpr\u00fcfen Sie Ihre Umleitungen und Dateinamen! diff --git a/src/de/todesbaum/jsite/i18n/jSite_fr.properties b/src/de/todesbaum/jsite/i18n/jSite_fr.properties deleted file mode 100644 index 39f9acd..0000000 --- a/src/de/todesbaum/jsite/i18n/jSite_fr.properties +++ /dev/null @@ -1,195 +0,0 @@ -# -# jSite - jSite_fr.properties - Copyright © 2006–2012 David Roden -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# - -# French language file by Florent Daigni\u00e8re , Julien Cornuwel , and Clement Vollet . - -# Attention, translators! Most of the strings here are used directly. -# However, some of them are parsed by MessageFormat -# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html) -# and thus have to adhere to some rules (check the URL above). This is the -# case when a line contains placeholders like {0} or {0,number}! In these -# lines single quotes (ASCII 39) needs to be escaped by entering them twice, -# otherwise the placeholder will not be replaced! - -jsite.general.ok=OK -jsite.general.cancel=Annuler - -jsite.wizard.previous=Pr\u00e9c\u00e9dent -jsite.wizard.next=Suivant -jsite.wizard.quit=Quitter - -jsite.quit.question=Voulez-vous r\u00e9ellement quitter? -jsite.quit.question.title=Souhaitez vous quitter? -jsite.quit.overwrite-configuration=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 -jsite.menu.language.en=English -jsite.menu.language.de=Deutsch -jsite.menu.language.fr=Fran\u00e7ais -jsite.menu.language.it=Italiano -jsite.menu.language.pl=Polski -jsite.menu.nodes=Noeud -jsite.menu.nodes.manage-nodes=G\u00e9rer les noeuds -jsite.menu.options=Options -jsite.menu.options.preferences=Pr\u00e9f\u00e9rences -jsite.menu.help=Aide -jsite.menu.help.check-for-updates=Mises \u00e0 jour -jsite.menu.help.about=A propos de jSite - -jsite.about.message=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. -jsite.node-manager.node-information=Information sur le noeud -jsite.node-manager.add-node=Ajouter un noeud -jsite.node-manager.new-node=Nouveau noeud -jsite.node-manager.delete-node=Supprimer un noeud -jsite.node-manager.delete-node.warning=Confirmer la suppression

Supprimer r\u00e9ellement ce noeud? -jsite.node-manager.name=Nom -jsite.node-manager.hostname=Nom de machine -jsite.node-manager.port=Port - -jsite.preferences.heading=Pr\u00e9f\u00e9rences -jsite.preferences.description=Utilisez cette page pour g\u00e9rer quelques param\u00e8tres globaux. -jsite.preferences.temp-directory=R\u00e9pertoire des fichiers temporaires -jsite.preferences.temp-directory.default=D\u00e9faut (choix syst\u00e8mes) -jsite.preferences.temp-directory.custom=Personnalis\u00e9 -jsite.preferences.temp-directory.choose=Choisir -jsite.preferences.temp-directory.choose.approve=Choisir -jsite.preferences.config-directory=Chemin du fichier de configuration -jsite.preferences.config-directory.jar=Suivant, le fichier JAR -jsite.preferences.config-directory.home=Acceuil -jsite.preferences.config-directory.custom=Répertoire personnel -jsite.preferences.insert-options=Options d'insertion -jsite.preferences.insert-options.use-early-encode=Générer d'abord l'URI -jsite.preferences.insert-options.priority=Priorité -jsite.preferences.insert-options.manifest-putter=Ajout de Manifest - -jsite.insert.heading=Projet d'insertion -jsite.insert.description=Veuillez attendre durant l'insertion du projet. -jsite.insert.project-information=Information \u00e0 propos du projet. -jsite.insert.request-uri=Freesite -jsite.insert.start-time=Commenc\u00e9 \u00e0 -jsite.insert.starting=D\u00e9marrage\u2026 -jsite.insert.done=Termin\u00e9. -jsite.insert.done.title=Insertion effectu\u00e9e -jsite.insert.insert-aborted=L'insertion a été annulée. -jsite.insert.insert-aborted.title=Insertion Annulée -jsite.insert.progress=Avancement -jsite.insert.k-per-s=Ko/s -jsite.insert.insert-failed=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. -jsite.insert.reinserted-edition.title=Edition r\u00e9ins\u00e9r\u00e9e - -jsite.file-scanner.can-not-read-directory=Impossible de lire le r\u00e9pertoire - -jsite.project.heading=S\u00e9lectionnez un projet -jsite.project.description=S\u00e9lectionnez un projet de la liste ou cr\u00e9ez en un nouveau. -jsite.project.action.browse=Naviguer -jsite.project.action.browse.choose=Choisir -jsite.project.action.browse.tooltip=Choisir un r\u00e9pertoire -jsite.project.action.add-project=Ajouter un projet -jsite.project.action.add-project.tooltip=Ajouter un projet -jsite.project.new-project.name=Nouveau projet -jsite.project.action.delete-project=D\u00e9truire le projet -jsite.project.action.delete-project.tooltip=D\u00e9truire le projet -jsite.project.action.delete-project.confirm=Confirmez la suppression

Le projet \u201c{0}\u201d va \u00eatre d\u00e9truit!
Voulez vous poursuivre? -jsite.project.action.clone-project=Cloner le projet -jsite.project.action.clone-project.copy=Copie de {0} -jsite.project.action.clone-project.tooltip=Cloner le projet s\u00e9lectionn\u00e9 -jsite.project.action.copy-uri=Copier l'URI dans le presse-papier -jsite.project.action.copy-uri.tooltip=Copie l'URI du projet dans le presse-papier -jsite.project.action.generate-new-key=G\u00e9n\u00e9rer une nouvelle cl\u00e9 -jsite.project.action.generate-new-key.tooltip=Cr\u00e9e une nouvelle cl\u00e9 pour ce projet -jsite.project.action.reset-edition=Remettre \u00e0 z\u00e9ro l'\u00e9dition -jsite.project.action.reset-edition.tooltip=Remettre \u00e0 z\u00e9ro l'\u00e9dition du projet -jsite.project.project.information=Informations concernant le projet -jsite.project.project.name=Nom -jsite.project.project.description=Description -jsite.project.project.local-path=Chemin local -jsite.project.project.address=Adresse -jsite.project.project.path=Chemin du freesite -jsite.project.project.edition=Edition -jsite.project.project.uri=URI -jsite.project.keygen.io-error=Erreur de communication avec le noeud

La communication avec le noeud \u00e0 \u00e9chou\u00e9e
Erreur:

{0}

Assurez vous que les informations saisies dans la page de configuration sont correctes. -jsite.project.warning.generate-new-key=G\u00e9n\u00e9rer une nouvelle cl\u00e9 ?

Si vous g\u00e9n\u00e9rez une nouvelle cl\u00e9, votre site sera publi\u00e9
avec cette nouvelle cl\u00e9. La confiance que les autres
utilisateurs pla\u00e7aient dans l'ancienne cl\u00e9 sera perdue ! -jsite.project.warning.reset-edition=Remettre \u00e0 z\u00e9ro l'\u00e9dition ?

Remettre \u00e0 z\u00e9ro l'\u00e9dition peut faire \u00e9chouer l'insertion
ou poser des probl\u00e8mes si vous n'avez pas chang\u00e9
le chemin ou les cl\u00e9s du projet ! -jsite.project.warning.use-clipboard-now=URI copi\u00e9e

Veuillez noter qu'il est possible qu'en quittant jSite
maintenant le presse-papiers soit vid\u00e9. Merci d'utiliser
l'URI copi\u00e9e imm\u00e9diatement dans une autre fen\u00eatre ! - -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.ignore-hidden-files=Ignorer les fichiers cach\u00e9s -jsite.project-files.ignore-hidden-files.tooltip=Si s\u00e9lectionn\u00e9, les fichiers cach\u00e9s ne sont pas ins\u00e9r\u00e9s -jsite.project-files.file-options=Option des fichiers -jsite.project-files.default=Fichier par d\u00e9faut -jsite.project-files.default.tooltip=Est-ce l'index? -jsite.project-files.insert=Ins\u00e9rer -jsite.project-files.insert.tooltip=D\u00e9cochez si vous ne voulez pas ins\u00e9rer ce fichier -jsite.project-files.force-insert=Forcer l'insertion -jsite.project-files.force-insert.tooltip=Forcer l'insertion de ce fichier tant qu'il n'est pas modifié -jsite.project-files.insert-redirect=Redirection -jsite.project-files.insert-redirect.tooltip=Cochez si vous voulez ins\u00e9rer une redirection pour ce fichier -jsite.project-files.custom-key=Clef existante -jsite.project-files.custom-key.tooltip=Utiliser une clef existante pour ce fichier -jsite.project-files.rename=Renommer -jsite.project-files.rename.tooltip=Renomme le fichier dans le site ins\u00e9r\u00e9 -jsite.project-files.mime-type=MIME type -jsite.project-files.mime-type.tooltip=S\u00e9lectionez le type MIME du fichier si la d\u00e9tection \u00e0 \u00e9chou\u00e9e -jsite.project-files.container=Container -jsite.project-files.container.tooltip=S\u00e9lectionnez un container pour le fichier -jsite.project-files.scan-error=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}) -jsite.update-checker.latest-version.title=Recherche de mises \u00e0 jour -jsite.update-checker.latest-version.newer.message=Vous utilisez la version {0} mais une version
plus r\u00e9cente ({1}) est disponible! -jsite.update-checker.latest-version.older.message=Vous utilisez la version {0} mais la
derni\u00e8re version semble \u00eatre la {1}. -jsite.update-checker.latest-version.okay.message=Vous utilisez la version {0}
qui semble \u00eatre la derni\u00e8re. - -jsite.key-dialog.title=G\u00e9rer les cl\u00e9s des projets -jsite.key-dialog.button.ok.tooltip=Accepter les changements -jsite.key-dialog.button.cancel.tooltip=Annuler les changements -jsite.key-dialog.button.generate=Reg\u00e9n\u00e9rer les cl\u00e9s -jsite.key-dialog.button.generate.tooltip=Cr\u00e9er une nouvelle paire de cl\u00e9s -jsite.key-dialog.label.keys=Cl\u00e9s -jsite.key-dialog.label.private-key=Cl\u00e9 priv\u00e9e -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.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! -jsite.error.no-node-running=Ce noeud n'est pas actif!

Vous ne pouvez pas utiliser jSite sans noeud actif.
Veuillez d\u00e9marrer votre noeud et r\u00e9essayer. -jsite.error.no-local-path=Pas de chemin local sp\u00e9cifi\u00e9

Vous avez omis de sp\u00e9cifier le chemin local \u00e0 ins\u00e9rer. -jsite.error.no-path=Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite

Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite.
Ce champ est n\u00e9cessaire. -jsite.error.index-missing=Votre fichier par d\u00e9faut est manquant

Un fichier par d\u00e9faut est sp\u00e9cifi\u00e9 mais il
n'existe plus ! S\u00e9lectionnez un nouveau
fichier par d\u00e9faut dans la liste. -jsite.error.index-not-inserted=Fichier par d\u00e9faut non ins\u00e9r\u00e9

Vous avez choisi de ne pas ins\u00e9rer le fichier par d\u00e9faut !
Vous devez soit l'ins\u00e9rer soit choisir
un fichier par d\u00e9faut diff\u00e9rent ! -jsite.error.no-custom-key=Pas de clef existante sp\u00e9cifi\u00e9e pour ce fichier

Vous avez sp\u00e9cifier de ne pas ins\u00e9rer {0}
mais n'avez pas sp\u00e9cifier de clef ou rediriger! -jsite.error.no-files-to-insert=Aucun fichier \u00e0 ins\u00e9rer

Vous n'avez s\u00e9lectionn\u00e9 aucun fichier pour l'insertion !
Veuillez s\u00e9lectionner au moins un fichier \u00e0 ins\u00e9rer. -jsite.error.duplicate-file=Fichier dupliqu\u00e9

Le fichier {0} est ins\u00e9r\u00e9 deux fois !
Veuillez v\u00e9rifier les noms de fichier et les redirections. diff --git a/src/de/todesbaum/jsite/main/CLI.java b/src/de/todesbaum/jsite/main/CLI.java deleted file mode 100644 index 65c9253..0000000 --- a/src/de/todesbaum/jsite/main/CLI.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * jSite - CLI.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.main; - -import java.io.PrintWriter; - -import de.todesbaum.jsite.application.Freenet7Interface; -import de.todesbaum.jsite.application.InsertListener; -import de.todesbaum.jsite.application.Node; -import de.todesbaum.jsite.application.Project; -import de.todesbaum.jsite.application.ProjectInserter; -import de.todesbaum.util.io.StreamCopier.ProgressListener; - -/** - * Command-line interface for jSite. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class CLI implements InsertListener { - - /** Object used for synchronization. */ - private Object lockObject = new Object(); - - /** Writer for the console. */ - private PrintWriter outputWriter = new PrintWriter(System.out, true); - - /** The freenet interface. */ - private Freenet7Interface freenetInterface; - - /** The project inserter. */ - private ProjectInserter projectInserter = new ProjectInserter(); - - /** The list of nodes. */ - private Node[] nodes; - - /** The projects. */ - private Project[] projects; - - /** Whether the insert has finished. */ - private boolean finished = false; - - /** Whether the insert finished successfully. */ - private boolean success; - - /** - * Creates a new command-line interface. - * - * @param args - * The command-line arguments - */ - private CLI(String[] args) { - - if ((args.length == 0) || args[0].equals("-h") || args[0].equals("--help")) { - outputWriter.println("\nParameters:\n"); - outputWriter.println(" --config-file="); - outputWriter.println(" --node="); - outputWriter.println(" --project="); - outputWriter.println(" --local-directory="); - outputWriter.println(" --path="); - outputWriter.println(" --edition="); - outputWriter.println("\nA project gets inserted when a new project is loaded on the command line,"); - outputWriter.println("or when the command line is finished. --local-directory, --path, and --edition"); - outputWriter.println("override the parameters in the project."); - return; - } - - String configFile = System.getProperty("user.home") + "/.jSite/config7"; - for (String argument : args) { - String value = argument.substring(argument.indexOf('=') + 1).trim(); - if (argument.startsWith("--config-file=")) { - configFile = value; - } - } - - ConfigurationLocator configurationLocator = new ConfigurationLocator(); - if (configFile != null) { - configurationLocator.setCustomLocation(configFile); - } - Configuration configuration = new Configuration(configurationLocator, configurationLocator.findPreferredLocation()); - - projectInserter.addInsertListener(this); - projects = configuration.getProjects(); - Node node = configuration.getSelectedNode(); - nodes = configuration.getNodes(); - - freenetInterface = new Freenet7Interface(); - freenetInterface.setNode(node); - - projectInserter.setFreenetInterface(freenetInterface); - - Project currentProject = null; - for (String argument : args) { - if (argument.startsWith("--config-file=")) { - /* we already parsed this one. */ - continue; - } - String value = argument.substring(argument.indexOf('=') + 1).trim(); - if (argument.startsWith("--node=")) { - Node newNode = getNode(value); - if (newNode == null) { - outputWriter.println("Node \"" + value + "\" not found."); - return; - } - node = newNode; - freenetInterface.setNode(node); - } else if (argument.startsWith("--project=")) { - if (currentProject != null) { - if (insertProject(currentProject)) { - outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted."); - } else { - outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted."); - } - currentProject = null; - } - currentProject = getProject(value); - if (currentProject == null) { - outputWriter.println("Project \"" + value + "\" not found."); - } - } else if (argument.startsWith("--local-directory")) { - if (currentProject == null) { - outputWriter.println("You can't specifiy --local-directory before --project."); - return; - } - currentProject.setLocalPath(value); - } else if (argument.startsWith("--path=")) { - if (currentProject == null) { - outputWriter.println("You can't specify --path before --project."); - return; - } - currentProject.setPath(value); - } else if (argument.startsWith("--edition=")) { - if (currentProject == null) { - outputWriter.println("You can't specify --edition before --project."); - return; - } - currentProject.setEdition(Integer.parseInt(value)); - } else { - outputWriter.println("Unknown parameter: " + argument); - return; - } - } - - int errorCode = 1; - if (currentProject != null) { - if (insertProject(currentProject)) { - outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted."); - errorCode = 0; - } else { - outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted."); - } - } - - configuration.setProjects(projects); - configuration.save(); - - System.exit(errorCode); - } - - /** - * Returns the project with the given name. - * - * @param name - * The name of the project - * @return The project, or null if no project could be found - */ - private Project getProject(String name) { - for (Project project : projects) { - if (project.getName().equals(name)) { - return project; - } - } - return null; - } - - /** - * Returns the node with the given name. - * - * @param name - * The name of the node - * @return The node, or null if no node could be found - */ - private Node getNode(String name) { - for (Node node : nodes) { - if (node.getName().equals(name)) { - return node; - } - } - return null; - } - - /** - * Inserts the given project. - * - * @param currentProject - * The project to insert - * @return true if the insert finished successfully, - * false otherwise - */ - private boolean insertProject(Project currentProject) { - if (!freenetInterface.hasNode()) { - outputWriter.println("Node is not running!"); - return false; - } - projectInserter.setProject(currentProject); - projectInserter.start(new ProgressListener() { - - public void onProgress(long copied, long length) { - System.out.print("Uploaded: " + copied + " / " + length + " bytes...\r"); - } - }); - synchronized (lockObject) { - while (!finished) { - try { - lockObject.wait(); - } catch (InterruptedException e) { - /* ignore, we're in a loop. */ - } - } - } - return success; - } - - // - // INTERFACE InsertListener - // - - /** - * {@inheritDoc} - */ - public void projectInsertStarted(Project project) { - outputWriter.println("Starting Insert of project \"" + project.getName() + "\"."); - } - - /** - * {@inheritDoc} - */ - public void projectUploadFinished(Project project) { - outputWriter.println("Project \"" + project.getName() + "\" has been uploaded, starting insert..."); - } - - /** - * {@inheritDoc} - */ - public void projectURIGenerated(Project project, String uri) { - outputWriter.println("URI: " + uri); - } - - /** - * {@inheritDoc} - */ - public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized) { - outputWriter.println("Progress: " + succeeded + " done, " + failed + " failed, " + fatal + " fatal, " + total + " total" + (finalized ? " (finalized)" : "") + ", " + ((succeeded + failed + fatal) * 100 / total) + "%"); - } - - /** - * {@inheritDoc} - */ - public void projectInsertFinished(Project project, boolean success, Throwable cause) { - outputWriter.println("Request URI: " + project.getFinalRequestURI(0)); - finished = true; - this.success = success; - synchronized (lockObject) { - lockObject.notify(); - } - } - - // - // MAIN - // - - /** - * Creates a new command-line interface with the given arguments. - * - * @param args - * The command-line arguments - */ - public static void main(String[] args) { - new CLI(args); - } - -} diff --git a/src/de/todesbaum/jsite/main/Configuration.java b/src/de/todesbaum/jsite/main/Configuration.java deleted file mode 100644 index f5001b0..0000000 --- a/src/de/todesbaum/jsite/main/Configuration.java +++ /dev/null @@ -1,645 +0,0 @@ -/* - * jSite - Configuration.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.main; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Map.Entry; -import java.util.logging.Level; -import java.util.logging.Logger; - -import de.todesbaum.jsite.application.FileOption; -import de.todesbaum.jsite.application.Node; -import de.todesbaum.jsite.application.Project; -import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; -import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; -import de.todesbaum.util.freenet.fcp2.PriorityClass; -import de.todesbaum.util.io.Closer; -import de.todesbaum.util.io.StreamCopier; -import de.todesbaum.util.xml.SimpleXML; -import de.todesbaum.util.xml.XML; - -/** - * The configuration. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class Configuration { - - /** The root node of the configuration. */ - private SimpleXML rootNode; - - /** The configuration locator. */ - private final ConfigurationLocator configurationLocator; - - /** Where the configuration resides. */ - private ConfigurationLocation configurationLocation; - - /** - * Creates a new configuration that is read from the given file. - * - * @param configurationLocator - * The configuration locator - * @param configurationLocation - * The configuration directory - */ - public Configuration(ConfigurationLocator configurationLocator, ConfigurationLocation configurationLocation) { - this.configurationLocator = configurationLocator; - this.configurationLocation = configurationLocation; - readConfiguration(configurationLocator.getFile(configurationLocation)); - } - - // - // ACCESSORS - // - - /** - * Returns the configuration locator. - * - * @return The configuration locator - */ - public ConfigurationLocator getConfigurationLocator() { - return configurationLocator; - } - - /** - * Returns the location the configuration will be written to when calling - * {@link #save()}. - * - * @return The location the configuration will be written to - */ - public ConfigurationLocation getConfigurationDirectory() { - return configurationLocation; - } - - /** - * Sets the location the configuration will be written to when calling - * {@link #save()}. - * - * @param configurationLocation - * The location to write the configuration to - */ - public void setConfigurationLocation(ConfigurationLocation configurationLocation) { - this.configurationLocation = configurationLocation; - } - - /** - * Reads the configuration from the file. - * - * @param filename - * The name of the file to read the configuration from - */ - private void readConfiguration(String filename) { - Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Reading configuration from: " + filename); - if (filename != null) { - File configurationFile = new File(filename); - if (configurationFile.exists()) { - ByteArrayOutputStream fileByteOutputStream = null; - FileInputStream fileInputStream = null; - try { - fileByteOutputStream = new ByteArrayOutputStream(); - fileInputStream = new FileInputStream(configurationFile); - StreamCopier.copy(fileInputStream, fileByteOutputStream, configurationFile.length()); - fileByteOutputStream.close(); - byte[] fileBytes = fileByteOutputStream.toByteArray(); - rootNode = SimpleXML.fromDocument(XML.transformToDocument(fileBytes)); - return; - } catch (FileNotFoundException e) { - /* ignore. */ - } catch (IOException e) { - /* ignore. */ - } finally { - Closer.close(fileInputStream); - Closer.close(fileByteOutputStream); - } - } - } - rootNode = new SimpleXML("configuration"); - } - - /** - * Saves the configuration. - * - * @return true if the configuration could be saved, - * false otherwise - */ - public boolean save() { - Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Trying to save configuration to: " + configurationLocation); - File configurationFile = new File(configurationLocator.getFile(configurationLocation)); - if (!configurationFile.exists()) { - File configurationFilePath = configurationFile.getAbsoluteFile().getParentFile(); - if (!configurationFilePath.exists() && !configurationFilePath.mkdirs()) { - return false; - } - } - FileOutputStream fileOutputStream = null; - ByteArrayInputStream configurationInputStream = null; - try { - byte[] configurationBytes = XML.transformToByteArray(rootNode.getDocument()); - configurationInputStream = new ByteArrayInputStream(configurationBytes); - fileOutputStream = new FileOutputStream(configurationFile); - StreamCopier.copy(configurationInputStream, fileOutputStream, configurationBytes.length); - return true; - } catch (IOException ioe1) { - /* ignore. */ - } finally { - Closer.close(configurationInputStream); - Closer.close(fileOutputStream); - } - return false; - } - - /** - * Returns the value of a node. - * - * @param nodeNames - * The name of all nodes in the chain - * @param defaultValue - * The default value to return if the node could not be found - * @return The value of the node, or the default value if the node could not - * be found - */ - private String getNodeValue(String[] nodeNames, String defaultValue) { - SimpleXML node = rootNode; - int nodeIndex = 0; - while ((node != null) && (nodeIndex < nodeNames.length)) { - node = node.getNode(nodeNames[nodeIndex++]); - } - if (node == null) { - return defaultValue; - } - return node.getValue(); - } - - /** - * Returns the integer value of a node. - * - * @param nodeNames - * The names of all nodes in the chain - * @param defaultValue - * The default value to return if the node can not be found - * @return The parsed integer value, or the default value if the node can - * not be found or the value can not be parsed into an integer - */ - private int getNodeIntValue(String[] nodeNames, int defaultValue) { - try { - return Integer.parseInt(getNodeValue(nodeNames, String.valueOf(defaultValue))); - } catch (NumberFormatException nfe1) { - /* ignore. */ - } - return defaultValue; - } - - /** - * Returns the boolean value of a node. - * - * @param nodeNames - * The names of all nodes in the chain - * @param defaultValue - * The default value to return if the node can not be found - * @return The parsed boolean value, or the default value if the node can - * not be found - */ - private boolean getNodeBooleanValue(String[] nodeNames, boolean defaultValue) { - String nodeValue = getNodeValue(nodeNames, null); - if (nodeValue == null) { - return defaultValue; - } - return Boolean.parseBoolean(nodeValue); - } - - /** - * Returns the hostname of the node. - * - * @return The hostname of the node - * @deprecated Use {@link #getSelectedNode()} instead - */ - @Deprecated - public String getNodeAddress() { - return getNodeValue(new String[] { "node-address" }, "localhost"); - } - - /** - * Sets the hostname of the node. - * - * @param nodeAddress - * The hostname of the node - * @deprecated Use {@link #setSelectedNode(Node)} instead - */ - @Deprecated - public void setNodeAddress(String nodeAddress) { - rootNode.replace("node-address", nodeAddress); - } - - /** - * The port number of the node - * - * @return The port number of the node - * @deprecated Use {@link #getSelectedNode()} instead. - */ - @Deprecated - public int getNodePort() { - return getNodeIntValue(new String[] { "node-port" }, 9481); - } - - /** - * Sets the port number of the node. - * - * @param nodePort - * The port number of the node - * @deprecated Use {@link #setSelectedNode(Node)} instead - */ - @Deprecated - public void setNodePort(int nodePort) { - rootNode.replace("node-port", String.valueOf(nodePort)); - } - - /** - * Returns whether the node configuration page should be skipped on startup. - * - * @return true to skip the node configuration page on startup, - * false to show it - */ - public boolean isSkipNodePage() { - return getNodeBooleanValue(new String[] { "skip-node-page" }, false); - } - - /** - * Sets whether the node configuration page should be skipped on startup. - * - * @param skipNodePage - * true to skip the node configuration page on - * startup, false to show it - */ - public void setSkipNodePage(boolean skipNodePage) { - rootNode.replace("skip-node-page", String.valueOf(skipNodePage)); - } - - /** - * Returns all configured projects. - * - * @return A list of all projects - */ - public Project[] getProjects() { - List projects = new ArrayList(); - SimpleXML projectsNode = rootNode.getNode("project-list"); - if (projectsNode != null) { - SimpleXML[] projectNodes = projectsNode.getNodes("project"); - for (SimpleXML projectNode : projectNodes) { - try { - Project project = new Project(); - projects.add(project); - project.setDescription(projectNode.getNode("description").getValue("")); - String indexFile = projectNode.getNode("index-file").getValue(""); - if (indexFile.indexOf('/') > -1) { - indexFile = ""; - } - project.setIndexFile(indexFile); - project.setLastInsertionTime(Long.parseLong(projectNode.getNode("last-insertion-time").getValue("0"))); - project.setLocalPath(projectNode.getNode("local-path").getValue("")); - project.setName(projectNode.getNode("name").getValue("")); - project.setPath(projectNode.getNode("path").getValue("")); - if ((project.getPath() != null) && (project.getPath().indexOf("/") != -1)) { - project.setPath(project.getPath().replaceAll("/", "")); - } - project.setEdition(Integer.parseInt(projectNode.getNode("edition").getValue("0"))); - project.setInsertURI(projectNode.getNode("insert-uri").getValue("")); - project.setRequestURI(projectNode.getNode("request-uri").getValue("")); - if (projectNode.getNode("ignore-hidden-files") != null) { - project.setIgnoreHiddenFiles(Boolean.parseBoolean(projectNode.getNode("ignore-hidden-files").getValue("true"))); - } else { - project.setIgnoreHiddenFiles(true); - } - - /* load last insert hashes. */ - Map 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) { - String filename = fileOptionNode.getNode("filename").getValue(); - FileOption fileOption = project.getFileOption(filename); - fileOption.setInsert(Boolean.parseBoolean(fileOptionNode.getNode("insert").getValue())); - if (fileOptionNode.getNode("insert-redirect") != null) { - fileOption.setInsertRedirect(Boolean.parseBoolean(fileOptionNode.getNode("insert-redirect").getValue())); - } - fileOption.setCustomKey(fileOptionNode.getNode("custom-key").getValue("")); - if (fileOptionNode.getNode("changed-name") != null) { - fileOption.setChangedName(fileOptionNode.getNode("changed-name").getValue()); - } - fileOption.setMimeType(fileOptionNode.getNode("mime-type").getValue("")); - fileOptions.put(filename, fileOption); - } - } - project.setFileOptions(fileOptions); - } catch (NumberFormatException nfe1) { - nfe1.printStackTrace(); - } - } - } - return projects.toArray(new Project[projects.size()]); - } - - /** - * Sets the list of all projects. - * - * @param projects - * The list of all projects - */ - public void setProjects(Project[] projects) { - SimpleXML projectsNode = new SimpleXML("project-list"); - for (Project project : projects) { - SimpleXML projectNode = projectsNode.append("project"); - projectNode.append("edition", String.valueOf(project.getEdition())); - projectNode.append("description", project.getDescription()); - projectNode.append("index-file", project.getIndexFile()); - projectNode.append("last-insertion-time", String.valueOf(project.getLastInsertionTime())); - projectNode.append("local-path", project.getLocalPath()); - projectNode.append("name", project.getName()); - projectNode.append("path", project.getPath()); - projectNode.append("insert-uri", project.getInsertURI()); - projectNode.append("request-uri", project.getRequestURI()); - projectNode.append("ignore-hidden-files", String.valueOf(project.isIgnoreHiddenFiles())); - - /* store last insert hashes. */ - SimpleXML lastInsertHashesNode = projectNode.append("last-insert-hashes"); - for (Entry 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()) { - Entry entry = entries.next(); - FileOption fileOption = entry.getValue(); - if (fileOption.isCustom()) { - SimpleXML fileOptionNode = fileOptionsNode.append("file-option"); - fileOptionNode.append("filename", entry.getKey()); - fileOptionNode.append("insert", String.valueOf(fileOption.isInsert())); - fileOptionNode.append("insert-redirect", String.valueOf(fileOption.isInsertRedirect())); - fileOptionNode.append("custom-key", fileOption.getCustomKey()); - fileOptionNode.append("changed-name", fileOption.getChangedName()); - fileOptionNode.append("mime-type", fileOption.getMimeType()); - } - } - } - rootNode.replace(projectsNode); - } - - /** - * Returns the stored locale. - * - * @return The stored locale - */ - public Locale getLocale() { - String language = getNodeValue(new String[] { "i18n", "language" }, "en"); - String country = getNodeValue(new String[] { "i18n", "country" }, null); - if (country != null) { - return new Locale(language, country); - } - return new Locale(language); - } - - /** - * Sets the locale to store. - * - * @param locale - * The locale to store - */ - public void setLocale(Locale locale) { - SimpleXML i18nNode = new SimpleXML("i18n"); - if (locale.getCountry().length() != 0) { - i18nNode.append("country", locale.getCountry()); - } - i18nNode.append("language", locale.getLanguage()); - rootNode.replace(i18nNode); - return; - } - - /** - * Returns a list of configured nodes. - * - * @return The list of the configured nodes - */ - public Node[] getNodes() { - SimpleXML nodesNode = rootNode.getNode("nodes"); - if (nodesNode == null) { - String hostname = getNodeAddress(); - int port = getNodePort(); - if (hostname == null) { - hostname = "127.0.0.1"; - port = 9481; - } - return new Node[] { new Node(hostname, port, "Node") }; - } - SimpleXML[] nodeNodes = nodesNode.getNodes("node"); - Node[] returnNodes = new Node[nodeNodes.length]; - int nodeIndex = 0; - for (SimpleXML nodeNode : nodeNodes) { - String name = nodeNode.getNode("name").getValue(); - String hostname = nodeNode.getNode("hostname").getValue(); - int port = Integer.parseInt(nodeNode.getNode("port").getValue()); - Node node = new Node(hostname, port, name); - returnNodes[nodeIndex++] = node; - } - return returnNodes; - } - - /** - * Sets the list of configured nodes. - * - * @param nodes - * The list of configured nodes - */ - public void setNodes(Node[] nodes) { - SimpleXML nodesNode = new SimpleXML("nodes"); - for (Node node : nodes) { - SimpleXML nodeNode = nodesNode.append("node"); - nodeNode.append("name", node.getName()); - nodeNode.append("hostname", node.getHostname()); - nodeNode.append("port", String.valueOf(node.getPort())); - } - rootNode.replace(nodesNode); - rootNode.remove("node-address"); - rootNode.remove("node-port"); - } - - /** - * Sets the selected node. - * - * @param selectedNode - * The selected node - */ - public void setSelectedNode(Node selectedNode) { - SimpleXML selectedNodeNode = new SimpleXML("selected-node"); - selectedNodeNode.append("name", selectedNode.getName()); - selectedNodeNode.append("hostname", selectedNode.getHostname()); - selectedNodeNode.append("port", String.valueOf(selectedNode.getPort())); - rootNode.replace(selectedNodeNode); - } - - /** - * Returns the selected node. - * - * @return The selected node - */ - public Node getSelectedNode() { - SimpleXML selectedNodeNode = rootNode.getNode("selected-node"); - if (selectedNodeNode == null) { - String hostname = getNodeAddress(); - int port = getNodePort(); - if (hostname == null) { - hostname = "127.0.0.1"; - port = 9481; - } - return new Node(hostname, port, "Node"); - } - String name = selectedNodeNode.getNode("name").getValue(); - String hostname = selectedNodeNode.getNode("hostname").getValue(); - int port = Integer.valueOf(selectedNodeNode.getNode("port").getValue()); - return new Node(hostname, port, name); - } - - /** - * Returns the temp directory to use. - * - * @return The temp directoy, or {@code null} to use the default temp - * directory - */ - public String getTempDirectory() { - return getNodeValue(new String[] { "temp-directory" }, null); - } - - /** - * Sets the temp directory to use. - * - * @param tempDirectory - * The temp directory to use, or {@code null} to use the default - * temp directory - */ - public void setTempDirectory(String tempDirectory) { - if (tempDirectory != null) { - SimpleXML tempDirectoryNode = new SimpleXML("temp-directory"); - tempDirectoryNode.setValue(tempDirectory); - rootNode.replace(tempDirectoryNode); - } else { - rootNode.remove("temp-directory"); - } - } - - /** - * Returns whether to use the “early encode“ flag for the insert. - * - * @return {@code true} to set the “early encode” flag for the insert, - * {@code false} otherwise - */ - public boolean useEarlyEncode() { - return getNodeBooleanValue(new String[] { "use-early-encode" }, false); - } - - /** - * Sets whether to use the “early encode“ flag for the insert. - * - * @param useEarlyEncode - * {@code true} to set the “early encode” flag for the insert, - * {@code false} otherwise - * @return This configuration - */ - public Configuration setUseEarlyEncode(boolean useEarlyEncode) { - rootNode.replace("use-early-encode", String.valueOf(useEarlyEncode)); - return this; - } - - /** - * Returns the insert priority. - * - * @return The insert priority - */ - public PriorityClass getPriority() { - return PriorityClass.valueOf(getNodeValue(new String[] { "insert-priority" }, "interactive")); - } - - /** - * Sets the insert priority. - * - * @param priority - * The insert priority - * @return This configuration - */ - public Configuration setPriority(PriorityClass priority) { - rootNode.replace("insert-priority", priority.toString()); - return this; - } - - /** - * Returns the manifest putter. - * - * @return The manifest putter - */ - public ManifestPutter getManifestPutter() { - return ManifestPutter.valueOf(getNodeValue(new String[] { "manifest-putter" }, "simple").toUpperCase()); - } - - /** - * Sets the manifest putter. - * - * @param manifestPutter - * The manifest putter - * @return This configuration - */ - public Configuration setManifestPutter(ManifestPutter manifestPutter) { - rootNode.replace("manifest-putter", manifestPutter.name().toLowerCase()); - return this; - } - -} diff --git a/src/de/todesbaum/jsite/main/ConfigurationLocator.java b/src/de/todesbaum/jsite/main/ConfigurationLocator.java deleted file mode 100644 index 967f936..0000000 --- a/src/de/todesbaum/jsite/main/ConfigurationLocator.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * jSite - ConfigurationLocator.java - Copyright © 2011–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.main; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -/** - * Locator for configuration files in different places. - * - * @author David ‘Bombe’ Roden <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 deleted file mode 100644 index edbf5e4..0000000 --- a/src/de/todesbaum/jsite/main/Main.java +++ /dev/null @@ -1,753 +0,0 @@ -/* - * jSite - Main.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.main; - -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.IOException; -import java.text.MessageFormat; -import java.util.Date; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.logging.ConsoleHandler; -import java.util.logging.Handler; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.ButtonGroup; -import javax.swing.Icon; -import javax.swing.JList; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JRadioButtonMenuItem; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; - -import de.todesbaum.jsite.application.Freenet7Interface; -import de.todesbaum.jsite.application.Node; -import de.todesbaum.jsite.application.Project; -import de.todesbaum.jsite.application.ProjectInserter; -import de.todesbaum.jsite.application.ProjectInserter.CheckReport; -import de.todesbaum.jsite.application.ProjectInserter.Issue; -import de.todesbaum.jsite.application.UpdateChecker; -import de.todesbaum.jsite.application.UpdateListener; -import de.todesbaum.jsite.gui.NodeManagerListener; -import de.todesbaum.jsite.gui.NodeManagerPage; -import de.todesbaum.jsite.gui.PreferencesPage; -import de.todesbaum.jsite.gui.ProjectFilesPage; -import de.todesbaum.jsite.gui.ProjectInsertPage; -import de.todesbaum.jsite.gui.ProjectPage; -import de.todesbaum.jsite.i18n.I18n; -import de.todesbaum.jsite.i18n.I18nContainer; -import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; -import de.todesbaum.util.image.IconLoader; -import de.todesbaum.util.swing.TWizard; -import de.todesbaum.util.swing.TWizardPage; -import de.todesbaum.util.swing.WizardListener; - -/** - * The main class that ties together everything. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class Main implements ActionListener, ListSelectionListener, WizardListener, NodeManagerListener, UpdateListener { - - /** The logger. */ - private static final Logger logger = Logger.getLogger(Main.class.getName()); - - /** The version. */ - private static final Version VERSION = new Version(0, 10); - - /** The configuration. */ - private Configuration configuration; - - /** The freenet interface. */ - private Freenet7Interface freenetInterface = new Freenet7Interface(); - - /** The update checker. */ - private final UpdateChecker updateChecker; - - /** The jSite icon. */ - private Icon jSiteIcon; - - /** - * Enumeration for all possible pages. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ - private static enum PageType { - - /** The node manager page. */ - PAGE_NODE_MANAGER, - - /** The project page. */ - PAGE_PROJECTS, - - /** The project files page. */ - PAGE_PROJECT_FILES, - - /** The project insert page. */ - PAGE_INSERT_PROJECT, - - /** The preferences page. */ - PAGE_PREFERENCES - - } - - /** The supported locales. */ - private static final Locale[] SUPPORTED_LOCALES = new Locale[] { Locale.ENGLISH, Locale.GERMAN, Locale.FRENCH }; - - /** The actions that switch the language. */ - private Map languageActions = new HashMap(); - - /** The “manage nodes” action. */ - private Action manageNodeAction; - - /** The “preferences” action. */ - private Action optionsPreferencesAction; - - /** The “check for updates” action. */ - private Action checkForUpdatesAction; - - /** The “about jSite” action. */ - private Action aboutAction; - - /** The wizard. */ - private TWizard wizard; - - /** The node menu. */ - private JMenu nodeMenu; - - /** The currently selected node. */ - private Node selectedNode; - - /** Mapping from page type to page. */ - private final Map pages = new HashMap(); - - /** The original location of the configuration file. */ - private ConfigurationLocation originalLocation; - - /** - * Creates a new core with the default configuration file. - */ - private Main() { - this(null); - } - - /** - * Creates a new core with the given configuration from the given file. - * - * @param configFilename - * The name of the configuration file - */ - private Main(String configFilename) { - /* collect all possible configuration file locations. */ - ConfigurationLocator configurationLocator = new ConfigurationLocator(); - if (configFilename != null) { - configurationLocator.setCustomLocation(configFilename); - } - - originalLocation = configurationLocator.findPreferredLocation(); - logger.log(Level.CONFIG, "Using configuration from " + originalLocation + "."); - configuration = new Configuration(configurationLocator, originalLocation); - - Locale.setDefault(configuration.getLocale()); - I18n.setLocale(configuration.getLocale()); - wizard = new TWizard(); - createActions(); - wizard.setJMenuBar(createMenuBar()); - wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); - wizard.setPreviousEnabled(false); - wizard.setNextEnabled(true); - wizard.addWizardListener(this); - jSiteIcon = IconLoader.loadIcon("/jsite-icon.png"); - wizard.setIcon(jSiteIcon); - - updateChecker = new UpdateChecker(freenetInterface); - updateChecker.addUpdateListener(this); - updateChecker.start(); - - initPages(); - showPage(PageType.PAGE_PROJECTS); - } - - /** - * Creates all actions. - */ - private void createActions() { - for (final Locale locale : SUPPORTED_LOCALES) { - languageActions.put(locale, new AbstractAction(I18n.getMessage("jsite.menu.language." + locale.getLanguage()), IconLoader.loadIcon("/flag-" + locale.getLanguage() + ".png")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - switchLanguage(locale); - } - }); - } - manageNodeAction = new AbstractAction(I18n.getMessage("jsite.menu.nodes.manage-nodes")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - showPage(PageType.PAGE_NODE_MANAGER); - optionsPreferencesAction.setEnabled(true); - wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); - wizard.setNextName(I18n.getMessage("jsite.wizard.next")); - } - }; - optionsPreferencesAction = new AbstractAction(I18n.getMessage("jsite.menu.options.preferences")) { - - /** - * {@inheritDoc} - */ - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - optionsPreferences(); - } - }; - checkForUpdatesAction = new AbstractAction(I18n.getMessage("jsite.menu.help.check-for-updates")) { - - /** - * {@inheritDoc} - */ - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent actionEvent) { - showLatestUpdate(); - } - }; - aboutAction = new AbstractAction(I18n.getMessage("jsite.menu.help.about")) { - - @SuppressWarnings("synthetic-access") - public void actionPerformed(ActionEvent e) { - JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.about.message"), getVersion().toString()), null, JOptionPane.INFORMATION_MESSAGE, jSiteIcon); - } - }; - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - manageNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.nodes.manage-nodes")); - optionsPreferencesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.options.preferences")); - checkForUpdatesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.check-for-updates")); - aboutAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.about")); - } - }); - } - - /** - * Creates the menu bar. - * - * @return The menu bar - */ - private JMenuBar createMenuBar() { - JMenuBar menuBar = new JMenuBar(); - final JMenu languageMenu = new JMenu(I18n.getMessage("jsite.menu.languages")); - menuBar.add(languageMenu); - ButtonGroup languageButtonGroup = new ButtonGroup(); - for (Locale locale : SUPPORTED_LOCALES) { - Action languageAction = languageActions.get(locale); - JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(languageActions.get(locale)); - if (locale.equals(Locale.getDefault())) { - menuItem.setSelected(true); - } - languageAction.putValue("menuItem", menuItem); - languageButtonGroup.add(menuItem); - languageMenu.add(menuItem); - } - nodeMenu = new JMenu(I18n.getMessage("jsite.menu.nodes")); - menuBar.add(nodeMenu); - selectedNode = configuration.getSelectedNode(); - nodesUpdated(configuration.getNodes()); - - final JMenu optionsMenu = new JMenu(I18n.getMessage("jsite.menu.options")); - menuBar.add(optionsMenu); - optionsMenu.add(optionsPreferencesAction); - - /* evil hack to right-align the help menu */ - JPanel panel = new JPanel(); - panel.setOpaque(false); - menuBar.add(panel); - - final JMenu helpMenu = new JMenu(I18n.getMessage("jsite.menu.help")); - menuBar.add(helpMenu); - helpMenu.add(checkForUpdatesAction); - helpMenu.add(aboutAction); - - I18nContainer.getInstance().registerRunnable(new Runnable() { - - @SuppressWarnings("synthetic-access") - public void run() { - languageMenu.setText(I18n.getMessage("jsite.menu.languages")); - nodeMenu.setText(I18n.getMessage("jsite.menu.nodes")); - optionsMenu.setText(I18n.getMessage("jsite.menu.options")); - helpMenu.setText(I18n.getMessage("jsite.menu.help")); - for (Map.Entry languageActionEntry : languageActions.entrySet()) { - languageActionEntry.getValue().putValue(Action.NAME, I18n.getMessage("jsite.menu.language." + languageActionEntry.getKey().getLanguage())); - } - } - }); - - return menuBar; - } - - /** - * Initializes all pages. - */ - private void initPages() { - NodeManagerPage nodeManagerPage = new NodeManagerPage(wizard); - nodeManagerPage.setName("page.node-manager"); - nodeManagerPage.addNodeManagerListener(this); - nodeManagerPage.setNodes(configuration.getNodes()); - pages.put(PageType.PAGE_NODE_MANAGER, nodeManagerPage); - - ProjectPage projectPage = new ProjectPage(wizard); - projectPage.setName("page.project"); - projectPage.setProjects(configuration.getProjects()); - projectPage.setFreenetInterface(freenetInterface); - projectPage.addListSelectionListener(this); - pages.put(PageType.PAGE_PROJECTS, projectPage); - - ProjectFilesPage projectFilesPage = new ProjectFilesPage(wizard); - projectFilesPage.setName("page.project.files"); - pages.put(PageType.PAGE_PROJECT_FILES, projectFilesPage); - - ProjectInsertPage projectInsertPage = new ProjectInsertPage(wizard); - projectInsertPage.setName("page.project.insert"); - projectInsertPage.setFreenetInterface(freenetInterface); - pages.put(PageType.PAGE_INSERT_PROJECT, projectInsertPage); - - PreferencesPage preferencesPage = new PreferencesPage(wizard); - preferencesPage.setName("page.preferences"); - preferencesPage.setTempDirectory(configuration.getTempDirectory()); - pages.put(PageType.PAGE_PREFERENCES, preferencesPage); - } - - /** - * Shows the page with the given type. - * - * @param pageType - * The page type to show - */ - private void showPage(PageType pageType) { - wizard.setPreviousEnabled(pageType.ordinal() > 0); - wizard.setNextEnabled(pageType.ordinal() < (pages.size() - 1)); - wizard.setPage(pages.get(pageType)); - wizard.setTitle(pages.get(pageType).getHeading() + " - jSite"); - } - - /** - * Returns whether a configuration file would be overwritten when calling - * {@link #saveConfiguration()}. - * - * @return {@code true} if {@link #saveConfiguration()} would overwrite an - * existing file, {@code false} otherwise - */ - private boolean isOverwritingConfiguration() { - return configuration.getConfigurationLocator().hasFile(configuration.getConfigurationDirectory()); - } - - /** - * Saves the configuration. - * - * @return true if the configuration could be saved, - * false otherwise - */ - private boolean saveConfiguration() { - NodeManagerPage nodeManagerPage = (NodeManagerPage) pages.get(PageType.PAGE_NODE_MANAGER); - configuration.setNodes(nodeManagerPage.getNodes()); - if (selectedNode != null) { - configuration.setSelectedNode(selectedNode); - } - - ProjectPage projectPage = (ProjectPage) pages.get(PageType.PAGE_PROJECTS); - configuration.setProjects(projectPage.getProjects()); - - PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES); - configuration.setTempDirectory(preferencesPage.getTempDirectory()); - - return configuration.save(); - } - - /** - * Finds a supported locale for the given locale. - * - * @param forLocale - * The locale to find a supported locale for - * @return The supported locale that was found, or the default locale if no - * supported locale could be found - */ - private Locale findSupportedLocale(Locale forLocale) { - for (Locale locale : SUPPORTED_LOCALES) { - if (locale.equals(forLocale)) { - return locale; - } - } - for (Locale locale : SUPPORTED_LOCALES) { - if (locale.getCountry().equals(forLocale.getCountry()) && locale.getLanguage().equals(forLocale.getLanguage())) { - return locale; - } - } - for (Locale locale : SUPPORTED_LOCALES) { - if (locale.getLanguage().equals(forLocale.getLanguage())) { - return locale; - } - } - return SUPPORTED_LOCALES[0]; - } - - /** - * Returns the version. - * - * @return The version - */ - public static final Version getVersion() { - return VERSION; - } - - // - // ACTIONS - // - - /** - * Switches the language of the interface to the given locale. - * - * @param locale - * The locale to switch to - */ - private void switchLanguage(Locale locale) { - Locale supportedLocale = findSupportedLocale(locale); - Action languageAction = languageActions.get(supportedLocale); - JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) languageAction.getValue("menuItem"); - menuItem.setSelected(true); - I18n.setLocale(supportedLocale); - for (Runnable i18nRunnable : I18nContainer.getInstance()) { - try { - i18nRunnable.run(); - } catch (Throwable t) { - /* we probably shouldn't swallow this. */ - } - } - wizard.setPage(wizard.getPage()); - configuration.setLocale(supportedLocale); - } - - /** - * Shows a dialog with general preferences. - */ - private void optionsPreferences() { - ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setConfigurationLocation(configuration.getConfigurationDirectory()); - ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasNextToJarConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.NEXT_TO_JAR_FILE)); - ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasCustomConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.CUSTOM)); - ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setUseEarlyEncode(configuration.useEarlyEncode()); - ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setPriority(configuration.getPriority()); - ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setManifestPutter(configuration.getManifestPutter()); - showPage(PageType.PAGE_PREFERENCES); - optionsPreferencesAction.setEnabled(false); - wizard.setNextEnabled(true); - wizard.setNextName(I18n.getMessage("jsite.wizard.next")); - } - - /** - * Shows a dialog box that shows the last version that was found by the - * {@link UpdateChecker}. - */ - private void showLatestUpdate() { - Version latestVersion = updateChecker.getLatestVersion(); - int versionDifference = latestVersion.compareTo(VERSION); - if (versionDifference > 0) { - JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.newer.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE); - } else if (versionDifference < 0) { - JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.older.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE); - } else { - JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.okay.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE); - } - } - - // - // INTERFACE ListSelectionListener - // - - /** - * {@inheritDoc} - */ - public void valueChanged(ListSelectionEvent e) { - JList list = (JList) e.getSource(); - int selectedRow = list.getSelectedIndex(); - wizard.setNextEnabled(selectedRow > -1); - } - - // - // INTERFACE WizardListener - // - - /** - * {@inheritDoc} - */ - public void wizardNextPressed(TWizard wizard) { - String pageName = wizard.getPage().getName(); - if ("page.node-manager".equals(pageName)) { - showPage(PageType.PAGE_PROJECTS); - } else if ("page.project".equals(pageName)) { - ProjectPage projectPage = (ProjectPage) wizard.getPage(); - Project project = projectPage.getSelectedProject(); - if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) { - JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.warning.no-local-path"), null, JOptionPane.ERROR_MESSAGE); - return; - } - if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) { - JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.warning.no-path"), null, JOptionPane.ERROR_MESSAGE); - return; - } - ((ProjectFilesPage) pages.get(PageType.PAGE_PROJECT_FILES)).setProject(project); - ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).setProject(project); - showPage(PageType.PAGE_PROJECT_FILES); - } else if ("page.project.files".equals(pageName)) { - ProjectPage projectPage = (ProjectPage) pages.get(PageType.PAGE_PROJECTS); - Project project = projectPage.getSelectedProject(); - if (selectedNode == null) { - JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.error.no-node-selected"), null, JOptionPane.ERROR_MESSAGE); - return; - } - CheckReport checkReport = ProjectInserter.validateProject(project); - for (Issue issue : checkReport) { - if (issue.isFatal()) { - JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.ERROR_MESSAGE); - return; - } - if (JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) { - return; - } - } - boolean nodeRunning = false; - try { - nodeRunning = freenetInterface.isNodePresent(); - } catch (IOException e) { - /* ignore. */ - } - if (!nodeRunning) { - JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.error.no-node-running"), null, JOptionPane.ERROR_MESSAGE); - return; - } - configuration.save(); - showPage(PageType.PAGE_INSERT_PROJECT); - ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT); - String tempDirectory = ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).getTempDirectory(); - projectInsertPage.setTempDirectory(tempDirectory); - projectInsertPage.setUseEarlyEncode(configuration.useEarlyEncode()); - projectInsertPage.setPriority(configuration.getPriority()); - projectInsertPage.setManifestPutter(configuration.getManifestPutter()); - projectInsertPage.startInsert(); - nodeMenu.setEnabled(false); - optionsPreferencesAction.setEnabled(false); - } else if ("page.project.insert".equals(pageName)) { - ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT); - if (projectInsertPage.isRunning()) { - projectInsertPage.stopInsert(); - } else { - showPage(PageType.PAGE_PROJECTS); - nodeMenu.setEnabled(true); - optionsPreferencesAction.setEnabled(true); - } - } else if ("page.preferences".equals(pageName)) { - PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES); - showPage(PageType.PAGE_PROJECTS); - optionsPreferencesAction.setEnabled(true); - configuration.setUseEarlyEncode(preferencesPage.useEarlyEncode()); - configuration.setPriority(preferencesPage.getPriority()); - configuration.setManifestPutter(preferencesPage.getManifestPutter()); - configuration.setConfigurationLocation(preferencesPage.getConfigurationLocation()); - } - } - - /** - * {@inheritDoc} - */ - public void wizardPreviousPressed(TWizard wizard) { - String pageName = wizard.getPage().getName(); - if ("page.project".equals(pageName) || "page.preferences".equals(pageName)) { - showPage(PageType.PAGE_NODE_MANAGER); - optionsPreferencesAction.setEnabled(true); - } else if ("page.project.files".equals(pageName)) { - showPage(PageType.PAGE_PROJECTS); - } else if ("page.project.insert".equals(pageName)) { - showPage(PageType.PAGE_PROJECT_FILES); - } - } - - /** - * {@inheritDoc} - */ - public void wizardQuitPressed(TWizard wizard) { - if (((ProjectPage) pages.get(PageType.PAGE_PROJECTS)).wasUriCopied() || ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).wasUriCopied()) { - JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project.warning.use-clipboard-now")); - } - if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.question"), I18n.getMessage("jsite.quit.question.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) { - if (isOverwritingConfiguration() && !originalLocation.equals(configuration.getConfigurationDirectory())) { - int overwriteConfigurationAnswer = JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.quit.overwrite-configuration"), configuration.getConfigurationLocator().getFile(configuration.getConfigurationDirectory())), I18n.getMessage("jsite.quit.overwrite-configuration.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); - if (overwriteConfigurationAnswer == JOptionPane.YES_OPTION) { - if (saveConfiguration()) { - System.exit(0); - } - } else if (overwriteConfigurationAnswer == JOptionPane.CANCEL_OPTION) { - return; - } - if (overwriteConfigurationAnswer == JOptionPane.NO_OPTION) { - System.exit(0); - } - } else { - if (saveConfiguration()) { - System.exit(0); - } - } - if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.config-not-saved"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { - System.exit(0); - } - } - } - - // - // INTERFACE NodeManagerListener - // - - /** - * {@inheritDoc} - */ - public void nodesUpdated(Node[] nodes) { - nodeMenu.removeAll(); - ButtonGroup nodeButtonGroup = new ButtonGroup(); - Node newSelectedNode = null; - for (Node node : nodes) { - JRadioButtonMenuItem nodeMenuItem = new JRadioButtonMenuItem(node.getName()); - nodeMenuItem.putClientProperty("Node", node); - nodeMenuItem.addActionListener(this); - nodeButtonGroup.add(nodeMenuItem); - if (node.equals(selectedNode)) { - newSelectedNode = node; - nodeMenuItem.setSelected(true); - } - nodeMenu.add(nodeMenuItem); - } - nodeMenu.addSeparator(); - nodeMenu.add(manageNodeAction); - selectedNode = newSelectedNode; - freenetInterface.setNode(selectedNode); - } - - /** - * {@inheritDoc} - */ - public void nodeSelected(Node node) { - for (Component menuItem : nodeMenu.getMenuComponents()) { - if (menuItem instanceof JMenuItem) { - if (node.equals(((JMenuItem) menuItem).getClientProperty("Node"))) { - ((JMenuItem) menuItem).setSelected(true); - } - } - } - freenetInterface.setNode(node); - selectedNode = node; - } - - // - // INTERFACE ActionListener - // - - /** - * {@inheritDoc} - */ - public void actionPerformed(ActionEvent e) { - Object source = e.getSource(); - if (source instanceof JRadioButtonMenuItem) { - JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) source; - Node node = (Node) menuItem.getClientProperty("Node"); - selectedNode = node; - freenetInterface.setNode(selectedNode); - } - } - - // - // INTERFACE UpdateListener - // - - /** - * {@inheritDoc} - */ - public void foundUpdateData(Version foundVersion, long versionTimestamp) { - logger.log(Level.FINEST, "Found version {0} from {1,date}.", new Object[] { foundVersion, versionTimestamp }); - if (foundVersion.compareTo(VERSION) > 0) { - JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.found-version.message"), foundVersion.toString(), new Date(versionTimestamp)), I18n.getMessage("jsite.update-checker.found-version.title"), JOptionPane.INFORMATION_MESSAGE); - } - } - - // - // MAIN METHOD - // - - /** - * Main method that is called by the VM. - * - * @param args - * The command-line arguments - */ - public static void main(String[] args) { - /* initialize logger. */ - Logger logger = Logger.getLogger("de.todesbaum"); - Handler handler = new ConsoleHandler(); - logger.addHandler(handler); - String configFilename = null; - boolean nextIsConfigFilename = false; - for (String argument : args) { - if (nextIsConfigFilename) { - configFilename = argument; - nextIsConfigFilename = false; - } - if ("--help".equals(argument)) { - printHelp(); - return; - } else if ("--debug".equals(argument)) { - logger.setLevel(Level.ALL); - handler.setLevel(Level.ALL); - } else if ("--config-file".equals(argument)) { - nextIsConfigFilename = true; - } - } - if (nextIsConfigFilename) { - System.out.println("--config-file needs parameter!"); - return; - } - new Main(configFilename); - } - - /** - * Prints a small syntax help. - */ - private static void printHelp() { - System.out.println("--help\tshows this cruft"); - System.out.println("--debug\tenables some debug output"); - System.out.println("--config-file \tuse specified configuration file"); - } - -} diff --git a/src/de/todesbaum/jsite/main/Version.java b/src/de/todesbaum/jsite/main/Version.java deleted file mode 100644 index 977836c..0000000 --- a/src/de/todesbaum/jsite/main/Version.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * jSite - Version.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.jsite.main; - -/** - * Container for version information. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public class Version implements Comparable { - - /** The components of the version information. */ - private final int[] components; - - /** - * Creates a new version container with the given components. - * - * @param components - * The version components - */ - public Version(int... components) { - this.components = new int[components.length]; - System.arraycopy(components, 0, this.components, 0, components.length); - } - - /** - * Returns the number of version components. - * - * @return The number of version components - */ - public int size() { - return components.length; - } - - /** - * Returns the version component with the given index. - * - * @param index - * The index of the version component - * @return The version component - */ - public int getComponent(int index) { - return components[index]; - } - - /** - * Parses a version from the given string. - * - * @param versionString - * The version string to parse - * @return The parsed version, or null if the string could not - * be parsed - */ - public static Version parse(String versionString) { - String[] componentStrings = versionString.split("\\."); - int[] components = new int[componentStrings.length]; - int index = -1; - for (String componentString : componentStrings) { - try { - components[++index] = Integer.parseInt(componentString); - } catch (NumberFormatException nfe1) { - return null; - } - } - return new Version(components); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - StringBuilder versionString = new StringBuilder(); - for (int component : components) { - if (versionString.length() != 0) { - versionString.append('.'); - } - versionString.append(component); - } - return versionString.toString(); - } - - /** - * {@inheritDoc} - */ - public int compareTo(Version version) { - int lessComponents = Math.min(components.length, version.components.length); - for (int index = 0; index < lessComponents; index++) { - if (version.components[index] == components[index]) { - continue; - } - return components[index] - version.components[index]; - } - return components.length - version.components.length; - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/Client.java b/src/de/todesbaum/util/freenet/fcp2/Client.java deleted file mode 100644 index ede92d1..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/Client.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * jSite - Client.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import de.todesbaum.util.io.StreamCopier.ProgressListener; - -/** - * A Client executes {@link Command}s over a {@link Connection} to a - * {@link Node} and delivers resulting {@link Message}s. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class Client implements ConnectionListener { - - /** The connection this client operates on. */ - private final Connection connection; - - /** The identifiers the client filters messages for. */ - private List identifiers = new ArrayList(); - - /** The queued messages. */ - private final List messageQueue = new ArrayList(); - - /** Whether the client was disconnected. */ - private boolean disconnected = false; - - /** Whether to catch all messages from the connection. */ - private boolean catchAll = false; - - /** - * Creates a new client that operates on the specified connection. - * - * @param connection - * The connection to operate on - */ - public Client(Connection connection) { - this.connection = connection; - connection.addConnectionListener(this); - } - - /** - * Creates a new client that operates on the specified connection and - * immediately executes the specified command. - * - * @param connection - * The connection to operate on - * @param command - * The command to execute - * @throws IOException - * if an I/O error occurs - * @see #execute(Command) - */ - public Client(Connection connection, Command command) throws IOException { - this(connection); - execute(command); - } - - /** - * Returns whether this client catches all messages going over the - * connection. - * - * @return true if the client catches all messages, - * false otherwise - */ - public boolean isCatchAll() { - return catchAll; - } - - /** - * Sets whether this client catches all messages going over the connection. - * - * @param catchAll - * true if the client should catch all messages, - * false otherwise - */ - public void setCatchAll(boolean catchAll) { - this.catchAll = catchAll; - } - - /** - * Executes the specified command. This will also clear the queue of - * messages, discarding all messages that resulted from the previous command - * and have not yet been read. - * - * @param command - * The command to execute - * @throws IOException - * if an I/O error occurs - * @see #execute(Command, boolean) - */ - public void execute(Command command) throws IOException { - execute(command, true); - } - - /** - * Executes the specified command. This will also clear the queue of - * messages, discarding all messages that resulted from the previous - * command and have not yet been read. - * - * @param command - * The command to execute - * @param progressListener - * The progress listener for payload transfers - * @throws IOException - * if an I/O error occurs - * @see #execute(Command, boolean) - */ - public void execute(Command command, ProgressListener progressListener) throws IOException { - execute(command, true, progressListener); - } - - /** - * Executes the specified command and optionally clears the list of - * identifiers this clients listens to before starting the command. - * - * @param command - * The command to execute - * @param removeExistingIdentifiers - * If true, the list of identifiers that this - * clients listens to is cleared - * @throws IOException - * if an I/O error occurs - */ - public void execute(Command command, boolean removeExistingIdentifiers) throws IOException { - execute(command, removeExistingIdentifiers, null); - } - - /** - * Executes the specified command and optionally clears the list of - * identifiers this clients listens to before starting the command. - * - * @param command - * The command to execute - * @param removeExistingIdentifiers - * If 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) { - identifiers.clear(); - } - identifiers.add(command.getIdentifier()); - } - connection.execute(command, progressListener); - } - - /** - * Returns the next message, waiting endlessly for it, if need be. If you - * are not sure whether a message will arrive, better use - * {@link #readMessage(long)} to only wait for a specific time. - * - * @return The next message that resulted from the execution of the last - * command - * @see #readMessage(long) - * @see #execute(Command) - */ - public Message readMessage() { - return readMessage(0); - } - - /** - * Returns the next message. If the message queue is currently empty, at - * least maxWaitTime milliseconds will be waited for a - * message to arrive. - * - * @param maxWaitTime - * The minimum time to wait for a message, in milliseconds - * @return The message, or null if no message arrived in time - * or the client is currently disconnected - * @see #isDisconnected() - * @see Object#wait(long) - */ - public Message readMessage(long maxWaitTime) { - synchronized (messageQueue) { - if (disconnected) { - return null; - } - if (messageQueue.size() == 0) { - try { - messageQueue.wait(maxWaitTime); - } catch (InterruptedException ie1) { - } - } - if (messageQueue.size() > 0) { - return messageQueue.remove(0); - } - } - return null; - } - - /** - * Returns whether the client is currently disconnected. - * - * @return true if the client is disconnected, - * false otherwise - */ - public boolean isDisconnected() { - synchronized (messageQueue) { - return disconnected; - } - } - - /** - * {@inheritDoc} - */ - public void messageReceived(Connection connection, Message message) { - synchronized (messageQueue) { - if (catchAll || (message.getIdentifier().length() == 0) || identifiers.contains(message.getIdentifier())) { - messageQueue.add(message); - messageQueue.notify(); - } - } - } - - /** - * {@inheritDoc} - */ - public void connectionTerminated(Connection connection) { - synchronized (messageQueue) { - disconnected = true; - messageQueue.notify(); - } - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientGet.java b/src/de/todesbaum/util/freenet/fcp2/ClientGet.java deleted file mode 100644 index 5b340e4..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/ClientGet.java +++ /dev/null @@ -1,383 +0,0 @@ -/* - * jSite - ClientGet.java - Copyright © 2008–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.IOException; -import java.io.Writer; - -/** - * Implementation of the “ClientGet” command. - * - * @author David ‘BombeB Roden <bombe@freenetproject.org> - */ -public class ClientGet extends Command { - - private boolean ignoreDataStore; - private boolean dataStoreOnly; - private String uri; - private Verbosity verbosity = Verbosity.NONE; - private long maxSize = -1; - private long maxTempSize = -1; - private int maxRetries = -1; - private PriorityClass priorityClass = PriorityClass.INTERACTIVE; - private Persistence persistence = Persistence.CONNECTION; - private String clientToken; - private boolean global = false; - private ReturnType returnType = ReturnType.direct; - private boolean binaryBlob = false; - private String allowedMimeTypes = null; - private String filename = null; - private String tempFilename = null; - - /** - *Creates a new ClientGet command with the given request identifier. - * - * @param identifier - * The request identifier - */ - public ClientGet(String identifier) { - super("ClientGet", identifier); - } - - /** - * TODO - * - * @return - */ - public boolean isIgnoreDataStore() { - return ignoreDataStore; - } - - /** - * TODO - * - * @param ignoreDataStore - */ - public void setIgnoreDataStore(boolean ignoreDataStore) { - this.ignoreDataStore = ignoreDataStore; - } - - /** - * TODO - * - * @return - */ - public boolean isDataStoreOnly() { - return dataStoreOnly; - } - - /** - * TODO - * - * @param dataStoreOnly - */ - public void setDataStoreOnly(boolean dataStoreOnly) { - this.dataStoreOnly = dataStoreOnly; - } - - /** - * TODO - * - * @return - */ - public String getUri() { - return uri; - } - - /** - * TODO - * - * @param uri - */ - public void setUri(String uri) { - this.uri = uri; - } - - /** - * TODO - * - * @return - */ - public Verbosity getVerbosity() { - return verbosity; - } - - /** - * TODO - * - * @param verbosity - */ - public void setVerbosity(Verbosity verbosity) { - this.verbosity = verbosity; - } - - /** - * TODO - * - * @return - */ - public long getMaxSize() { - return maxSize; - } - - /** - * TODO - * - * @param maxSize - */ - public void setMaxSize(long maxSize) { - this.maxSize = maxSize; - } - - /** - * TODO - * - * @return - */ - public long getMaxTempSize() { - return maxTempSize; - } - - /** - * TODO - * - * @param maxTempSize - */ - public void setMaxTempSize(long maxTempSize) { - this.maxTempSize = maxTempSize; - } - - /** - * TODO - * - * @return - */ - public int getMaxRetries() { - return maxRetries; - } - - /** - * TODO - * - * @param maxRetries - */ - public void setMaxRetries(int maxRetries) { - this.maxRetries = maxRetries; - } - - /** - * TODO - * - * @return - */ - public PriorityClass getPriorityClass() { - return priorityClass; - } - - /** - * TODO - * - * @param priorityClass - */ - public void setPriorityClass(PriorityClass priorityClass) { - this.priorityClass = priorityClass; - } - - /** - * TODO - * - * @return - */ - public Persistence getPersistence() { - return persistence; - } - - /** - * TODO - * - * @param persistence - */ - public void setPersistence(Persistence persistence) { - this.persistence = persistence; - } - - /** - * TODO - * - * @return - */ - public String getClientToken() { - return clientToken; - } - - /** - * TODO - * - * @param clientToken - */ - public void setClientToken(String clientToken) { - this.clientToken = clientToken; - } - - /** - * TODO - * - * @return - */ - public boolean isGlobal() { - return global; - } - - /** - * TODO - * - * @param global - */ - public void setGlobal(boolean global) { - this.global = global; - } - - /** - * TODO - * - * @return - */ - public ReturnType getReturnType() { - return returnType; - } - - /** - * TODO - * - * @param returnType - */ - public void setReturnType(ReturnType returnType) { - this.returnType = returnType; - } - - /** - * TODO - * - * @return - */ - public boolean isBinaryBlob() { - return binaryBlob; - } - - /** - * TODO - * - * @param binaryBlob - */ - public void setBinaryBlob(boolean binaryBlob) { - this.binaryBlob = binaryBlob; - } - - /** - * TODO - * - * @return - */ - public String getAllowedMimeTypes() { - return allowedMimeTypes; - } - - /** - * TODO - * - * @param allowedMimeTypes - */ - public void setAllowedMimeTypes(String allowedMimeTypes) { - this.allowedMimeTypes = allowedMimeTypes; - } - - /** - * TODO - * - * @return - */ - public String getFilename() { - return filename; - } - - /** - * TODO - * - * @param filename - */ - public void setFilename(String filename) { - this.filename = filename; - } - - /** - * TODO - * - * @return - */ - public String getTempFilename() { - return tempFilename; - } - - /** - * TODO - * - * @param tempFilename - */ - public void setTempFilename(String tempFilename) { - this.tempFilename = tempFilename; - } - - /** - * {@inheritDoc} - */ - @Override - protected void write(Writer writer) throws IOException { - super.write(writer); - writer.write("IgnoreDS=" + ignoreDataStore + LINEFEED); - writer.write("DSonly=" + dataStoreOnly + LINEFEED); - writer.write("URI=" + uri + LINEFEED); - writer.write("Verbosity=" + verbosity.getValue() + LINEFEED); - if (maxSize > -1) { - writer.write("MaxSize=" + maxSize + LINEFEED); - } - if (maxTempSize > -1) { - writer.write("MaxTempSize=" + maxTempSize + LINEFEED); - } - if (maxRetries >= -1) { - writer.write("MaxRetries=" + maxRetries + LINEFEED); - } - writer.write("PriorityClass=" + priorityClass.getValue() + LINEFEED); - writer.write("Persistence=" + persistence.getName() + LINEFEED); - if (clientToken != null) { - writer.write("ClientToken=" + clientToken + LINEFEED); - } - writer.write("Global=" + global + LINEFEED); - writer.write("BinaryBlob=" + binaryBlob + LINEFEED); - if (allowedMimeTypes != null) { - writer.write("AllowedMIMETypes=" + allowedMimeTypes + LINEFEED); - } - if (returnType == ReturnType.disk) { - writer.write("Filename=" + filename + LINEFEED); - if (tempFilename != null) { - writer.write("TempFilename=" + tempFilename + LINEFEED); - } - } - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientHello.java b/src/de/todesbaum/util/freenet/fcp2/ClientHello.java deleted file mode 100644 index 5983078..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/ClientHello.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * jSite - ClientHello.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.IOException; -import java.io.Writer; - -/** - * Implementation of the ClientHello command. This command must - * be sent as the first command on a connection ({@link de.todesbaum.util.freenet.fcp2.Connection#connect()} - * takes care of that) and must not be sent afterwards. - *

- * The node can answer with the following messages: NodeHello. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class ClientHello extends Command { - - /** The name of the client. */ - protected String name; - - /** The version of the FCP protocol the client expects. */ - protected String expectedVersion = "2.0"; - - /** - * Creates a new ClientHello command. - */ - public ClientHello() { - super("ClientHello", "ClientHello-" + System.currentTimeMillis()); - } - - /** - * Returns the value of the ExpectedVersion parameter of this - * command. At the moment this value is not used by the node but in the - * future this may be used to enforce certain node versions. - * - * @return The expected version - */ - public String getExpectedVersion() { - return expectedVersion; - } - - /** - * Sets the value of the ExpectedVersion parameter of this - * command. At the moment this value is not used by the node but in the - * future this may be used to enforce certain node versions. - * - * @param expectedVersion - * The expected version - */ - public void setExpectedVersion(String expectedVersion) { - this.expectedVersion = expectedVersion; - } - - /** - * Returns the name of the client that is connecting. - * - * @return The name of the client - */ - public String getName() { - return name; - } - - /** - * Sets the name of the client that is connecting. - * - * @param name - * The name of the client - */ - public void setName(String name) { - this.name = name; - } - - /** - * {@inheritDoc} - */ - @Override - protected void write(Writer writer) throws IOException { - writer.write("Name=" + name + LINEFEED); - writer.write("ExpectedVersion=" + expectedVersion + LINEFEED); - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPut.java b/src/de/todesbaum/util/freenet/fcp2/ClientPut.java deleted file mode 100644 index 9993173..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/ClientPut.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * jSite - ClientPut.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.IOException; -import java.io.Writer; - -/** - * Abstract base class for all put requests. It contains all parameters that put - * requests have in common. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public abstract class ClientPut extends Command { - - /** The URI of this request. */ - protected final String uri; - - /** The client token of this request. */ - protected String clientToken = null; - - /** Whether this request should only create a CHK. */ - protected boolean getCHKOnly = false; - - /** Whether this request is a global request. */ - protected boolean global = false; - - /** Whether the node should not try to compress the file. */ - protected boolean dontCompress = false; - - /** The maximum number of retries of this command. */ - protected int maxRetries = 0; - - /** Whether to generate the keys early. */ - protected boolean earlyEncode = false; - - /** The persistence of this request. */ - protected Persistence persistence = Persistence.CONNECTION; - - /** The priority class of this request. */ - protected PriorityClass priorityClass = PriorityClass.INTERACTIVE; - - /** The verbosiry of this request. */ - protected Verbosity verbosity = Verbosity.NONE; - - /** - * Creates a new put request with the specified name, identifier and URI. - * - * @param name - * The name of this request - * @param identifier - * The identifier of this request - * @param uri - * The URI of this request - */ - protected ClientPut(String name, String identifier, String uri) { - super(name, identifier); - this.uri = uri; - } - - /** - * Returns whether the node should not try to compress the data. - * - * @return true if the node should not try - * to compress the data - */ - public boolean isDontCompress() { - return dontCompress; - } - - /** - * Sets whether the node should not try to compress the data. A client might - * set this hint on data that is clearly not compressible, like MPEG audio - * files, JPEG or PNG images, highly compressed movies, or compressed - * archives like ZIP files. Otherwise the node will try to compress the file - * which -- depending on the size of the data -- might take a lot of time - * and memory. - * - * @param dontCompress - * true if the node should not - * try to compress the data - */ - public void setDontCompress(boolean dontCompress) { - this.dontCompress = dontCompress; - } - - /** - * Returns whether this request should only return the CHK of the data. - * @return Whether this request should only return the CHK of the data - */ - public boolean isGetCHKOnly() { - return getCHKOnly; - } - - /** - * Sets whether this request should only return the CHK of the data. - * @param getCHKOnly - * true if this request should only return the CHK of the data - */ - public void setGetCHKOnly(boolean getCHKOnly) { - this.getCHKOnly = getCHKOnly; - } - - /** - * Returns whether this request is a global request. - * @return true if this request is a global request, false otherwise - */ - public boolean isGlobal() { - return global; - } - - /** - * Sets whether this request is a global request. - * @param global - * true if this request is a global request, false otherwise - */ - public void setGlobal(boolean global) { - this.global = global; - } - - /** - * Returns the maximum number of retries of this request. - * @return The maximum number of retries of this request - */ - public int getMaxRetries() { - return maxRetries; - } - - /** - * Sets the maximum number of retries of this request - * @param maxRetries - * The maximum number of retries of this request - */ - public void setMaxRetries(int maxRetries) { - this.maxRetries = maxRetries; - } - - /** - * Returns whether the data should be encoded early to generate the final - * key as fast as possible. - * - * @return {@code true} if the key should be generated early, {@code false} - * otherwise - */ - public boolean isEarlyEncode() { - return earlyEncode; - } - - /** - * Sets whether the data should be encoded early to generate the final key - * as fast as possible. - * - * @param earlyEncode - * {@code true} if the key should be generated early, {@code - * false} otherwise - */ - public void setEarlyEncode(boolean earlyEncode) { - this.earlyEncode = earlyEncode; - } - - /** - * Returns the priority class of this request. - * @return The priority class of this request - */ - public PriorityClass getPriorityClass() { - return priorityClass; - } - - /** - * Sets the priority class of this request. - * @param priorityClass - * The priority class of this request - */ - public void setPriorityClass(PriorityClass priorityClass) { - this.priorityClass = priorityClass; - } - - /** - * Returns the verbosity of this request. - * @return The verbosity of this request - */ - public Verbosity getVerbosity() { - return verbosity; - } - - /** - * Sets the verbosity of this request. - * @param verbosity - * The verbosity of this request - */ - public void setVerbosity(Verbosity verbosity) { - this.verbosity = verbosity; - } - - /** - * Returns the URI of this request - * @return The URI of this request. - */ - public String getUri() { - return uri; - } - - /** - * {@inheritDoc} - */ - @Override - protected void write(Writer writer) throws IOException { - super.write(writer); - writer.write("URI=" + uri + LINEFEED); - if (verbosity != null) - writer.write("Verbosity=" + verbosity.getValue() + LINEFEED); - if (maxRetries != 0) - writer.write("MaxRetries=" + maxRetries + LINEFEED); - writer.write("EarlyEncode=" + earlyEncode); - if (priorityClass != null) - writer.write("PriorityClass=" + priorityClass.getValue() + LINEFEED); - writer.write("GetCHKOnly=" + getCHKOnly + LINEFEED); - writer.write("Global=" + global + LINEFEED); - writer.write("DontCompress=" + dontCompress + LINEFEED); - if (clientToken != null) - writer.write("ClientToken=" + clientToken + LINEFEED); - if (persistence != null) - writer.write("Persistence=" + persistence.getName() + LINEFEED); - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java b/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java deleted file mode 100644 index d72f2c1..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java +++ /dev/null @@ -1,187 +0,0 @@ -/* - * jSite - ClientPutComplexDir.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.Writer; -import java.util.ArrayList; -import java.util.List; - -import de.todesbaum.util.io.Closer; - -/** - * Implementation of the ClientPutComplexDir command. This command - * can be used to insert directories that do not exist on disk. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class ClientPutComplexDir extends ClientPutDir { - - /** The file entries of this directory. */ - private List fileEntries = new ArrayList(); - - /** Whether this request has payload. */ - private boolean hasPayload = false; - - /** The input streams for the payload. */ - private File payloadFile; - - /** The total number of bytes of the payload. */ - private long payloadLength = 0; - - /** The temp directory to use. */ - private final String tempDirectory; - - /** - * Creates a new ClientPutComplexDir command with the specified - * identifier and URI. - * - * @param identifier - * The identifier of the command - * @param uri - * The URI of the command - */ - public ClientPutComplexDir(String identifier, String uri) { - this(identifier, uri, null); - } - - /** - * Creates a new ClientPutComplexDir command with the specified - * identifier and URI. - * - * @param identifier - * The identifier of the command - * @param uri - * The URI of the command - * @param tempDirectory - * The temp directory to use, or {@code null} to use the default - * temp directory - */ - public ClientPutComplexDir(String identifier, String uri, String tempDirectory) { - super("ClientPutComplexDir", identifier, uri); - this.tempDirectory = tempDirectory; - } - - /** - * Adds a file to the directory inserted by this request. - * - * @param fileEntry - * The file entry to add to the directory - * @throws IOException - * if an I/O error occurs when creating the payload stream - */ - public void addFileEntry(FileEntry fileEntry) throws IOException { - if (fileEntry instanceof DirectFileEntry) { - if (payloadFile == null) { - try { - payloadFile = File.createTempFile("payload", ".dat", (tempDirectory != null) ? new File(tempDirectory) : null); - payloadFile.deleteOnExit(); - } catch (IOException e) { - /* ignore. */ - } - } - if (payloadFile != null) { - InputStream payloadInputStream = ((DirectFileEntry) fileEntry).getDataInputStream(); - FileOutputStream payloadOutputStream = null; - try { - payloadOutputStream = new FileOutputStream(payloadFile, true); - byte[] buffer = new byte[65536]; - int read = 0; - while ((read = payloadInputStream.read(buffer)) != -1) { - payloadOutputStream.write(buffer, 0, read); - } - payloadOutputStream.flush(); - fileEntries.add(fileEntry); - } catch (IOException ioe1) { - payloadFile.delete(); - throw ioe1; - } finally { - Closer.close(payloadOutputStream); - Closer.close(payloadInputStream); - } - } - } else { - fileEntries.add(fileEntry); - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void write(Writer writer) throws IOException { - super.write(writer); - int fileIndex = 0; - for (FileEntry fileEntry : fileEntries) { - writer.write("Files." + fileIndex + ".Name=" + fileEntry.getFilename() + LINEFEED); - if (fileEntry.getContentType() != null) { - writer.write("Files." + fileIndex + ".Metadata.ContentType=" + fileEntry.getContentType() + LINEFEED); - } - writer.write("Files." + fileIndex + ".UploadFrom=" + fileEntry.getName() + LINEFEED); - if (fileEntry instanceof DirectFileEntry) { - hasPayload = true; - writer.write("Files." + fileIndex + ".DataLength=" + ((DirectFileEntry) fileEntry).getDataLength() + LINEFEED); - payloadLength += ((DirectFileEntry) fileEntry).getDataLength(); - } else if (fileEntry instanceof DiskFileEntry) { - writer.write("Files." + fileIndex + ".Filename=" + ((DiskFileEntry) fileEntry).getFilename() + LINEFEED); - } else if (fileEntry instanceof RedirectFileEntry) { - writer.write("Files." + fileIndex + ".TargetURI=" + ((RedirectFileEntry) fileEntry).getTargetURI() + LINEFEED); - } - fileIndex++; - } - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean hasPayload() { - return hasPayload; - } - - /** - * {@inheritDoc} - */ - @Override - protected long getPayloadLength() { - return payloadLength; - } - - /** - * {@inheritDoc} - */ - @Override - protected InputStream getPayload() { - if (payloadFile != null) { - try { - return new FileInputStream(payloadFile); - } catch (FileNotFoundException e) { - /* shouldn't occur. */ - } - } - return null; - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java b/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java deleted file mode 100644 index 386fe1d..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/ClientPutDir.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * jSite - ClientPutDir.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.IOException; -import java.io.Writer; - -/** - * Abstract base class for all put requests that insert a directory. - * - * @param - * The type of the “ClientPutDir” command - * @author David Roden <droden@gmail.com> - */ -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. - * - * @param name - * The name of the request - * @param identifier - * The identifier of the request - * @param uri - * The URI of the request - */ - public ClientPutDir(String name, String identifier, String uri) { - super(name, identifier, uri); - } - - /** - * Returns the default name of the directory. - * - * @return The default name of the directory - */ - public String getDefaultName() { - return defaultName; - } - - /** - * Sets the default name of the directory. The default name of a directory - * is the name of the file that will be delivered if the directory was - * requested without a filename. It's about the same as the - * index.html file that gets delivered if you only request a - * directory from a webserver. - * - * @param defaultName - * The default name of the directory - */ - public void setDefaultName(String defaultName) { - this.defaultName = defaultName; - } - - /** - * Returns the current manifest putter. - * - * @return The current manifest putter (may be {@code null}) - */ - public ManifestPutter getManifestPutter() { - return manifestPutter; - } - - /** - * Sets the manifest putter for the “ClientPutDir” command. If {@code null} - * is given the node will choose a manifest putter. - * - * @param manifestPutter - * The manifest putter to use for the command (may be - * {@code null}) - * @return This ClientPutDir command - */ - @SuppressWarnings("unchecked") - public C setManifestPutter(ManifestPutter manifestPutter) { - this.manifestPutter = manifestPutter; - return (C) this; - } - - /** - * {@inheritDoc} - */ - @Override - protected void write(Writer writer) throws IOException { - super.write(writer); - if (defaultName != null) - writer.write("DefaultName=" + defaultName + LINEFEED); - if (manifestPutter != null) { - writer.write("ManifestPutter=" + manifestPutter.getName() + LINEFEED); - } - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/Command.java b/src/de/todesbaum/util/freenet/fcp2/Command.java deleted file mode 100644 index 63aaa9c..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/Command.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * jSite - Command.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.IOException; -import java.io.InputStream; -import java.io.Writer; - -/** - * Abstract base class for all commands. - *

- * In addition to the replies listed at the type comment of each specific - * command the node can always send the following messages: - * ProtocolError (if this library screws up), - * CloseConnectionDuplicateClientName (if a client with the same - * name of the {@link de.todesbaum.util.freenet.fcp2.Connection} connects). So - * when receiving messages from the node you should always be prepared for - * something you did not expect. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public abstract class Command { - - /** The line feed sequence used by the library. */ - protected static final String LINEFEED = "\r\n"; - - /** - * The name of the command. The name is sent to the node so it can not be - * chosen arbitrarily! - */ - private final String commandName; - - /** - * The identifier of the command. This identifier is used to identify - * replies that are caused by a command. - */ - private final String identifier; - - /** - * Creates a new command with the specified name and identifier. - * - * @param name - * The name of the command - * @param identifier - * The identifier of the command - */ - public Command(String name, String identifier) { - this.commandName = name; - this.identifier = identifier; - } - - /** - * Returns the name of this command. - * - * @return The name of this command - */ - public String getCommandName() { - return commandName; - } - - /** - * Return the identifier of this command. - * - * @return The identifier of this command - */ - public String getIdentifier() { - return identifier; - } - - /** - * Writes all parameters to the specified writer. - *

- * NOTE: Subclasses of Command must call - * super.write(writer) before or after writing their own - * parameters! - * - * @param writer - * The stream to write the parameters to - * @throws IOException - * if an I/O error occurs - */ - protected void write(Writer writer) throws IOException { - if (identifier != null) - writer.write("Identifier=" + identifier + LINEFEED); - } - - /** - * Returns whether this command has payload to send after the message. - * Subclasses need to return true here if they need to send - * payload after the message. - * - * @return true if this command has payload to send, - * false otherwise - */ - protected boolean hasPayload() { - return false; - } - - /** - * Returns the payload of this command as an {@link InputStream}. This - * method is never called if {@link #hasPayload()} returns - * false. - * - * @return The payload of this command - */ - protected InputStream getPayload() { - return null; - } - - /** - * Returns the length of the payload. This method is never called if - * {@link #hasPayload()} returns false. - * - * @return The length of the payload - */ - protected long getPayloadLength() { - return -1; - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/Connection.java b/src/de/todesbaum/util/freenet/fcp2/Connection.java deleted file mode 100644 index c4cb669..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/Connection.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * jSite - Connection.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; -import java.net.Socket; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -import de.todesbaum.util.io.Closer; -import de.todesbaum.util.io.LineInputStream; -import de.todesbaum.util.io.StreamCopier; -import de.todesbaum.util.io.StreamCopier.ProgressListener; -import de.todesbaum.util.io.TempFileInputStream; - -/** - * A physical connection to a Freenet node. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class Connection { - - /** The listeners that receive events from this connection. */ - private List connectionListeners = new ArrayList(); - - /** The node this connection is connected to. */ - private final Node node; - - /** The name of this connection. */ - private final String name; - - /** The network socket of this connection. */ - private Socket nodeSocket; - - /** The input stream that reads from the socket. */ - private InputStream nodeInputStream; - - /** The output stream that writes to the socket. */ - private OutputStream nodeOutputStream; - - /** The thread that reads from the socket. */ - private NodeReader nodeReader; - - /** A writer for the output stream. */ - private Writer nodeWriter; - - /** The NodeHello message sent by the node on connect. */ - protected Message nodeHello; - - /** The temp directory to use. */ - private String tempDirectory; - - /** - * Creates a new connection to the specified node with the specified name. - * - * @param node - * The node to connect to - * @param name - * The name of this connection - */ - public Connection(Node node, String name) { - this.node = node; - this.name = name; - } - - /** - * Adds a listener that gets notified on connection events. - * - * @param connectionListener - * The listener to add - */ - public void addConnectionListener(ConnectionListener connectionListener) { - connectionListeners.add(connectionListener); - } - - /** - * Removes a listener from the list of registered listeners. Only the first - * matching listener is removed. - * - * @param connectionListener - * The listener to remove - * @see List#remove(java.lang.Object) - */ - public void removeConnectionListener(ConnectionListener connectionListener) { - connectionListeners.remove(connectionListener); - } - - /** - * Notifies listeners about a received message. - * - * @param message - * The received message - */ - protected void fireMessageReceived(Message message) { - for (ConnectionListener connectionListener : connectionListeners) { - connectionListener.messageReceived(this, message); - } - } - - /** - * Notifies listeners about the loss of the connection. - */ - protected void fireConnectionTerminated() { - for (ConnectionListener connectionListener : connectionListeners) { - connectionListener.connectionTerminated(this); - } - } - - /** - * Returns the name of the connection. - * - * @return The name of the connection - */ - public String getName() { - return name; - } - - /** - * Sets the temp directory to use for creation of temporary files. - * - * @param tempDirectory - * The temp directory to use, or {@code null} to use the default - * temp directory - */ - public void setTempDirectory(String tempDirectory) { - this.tempDirectory = tempDirectory; - } - - /** - * Connects to the node. - * - * @return true if the connection succeeded and the node - * returned a NodeHello message - * @throws IOException - * if an I/O error occurs - * @see #getNodeHello() - */ - public synchronized boolean connect() throws IOException { - nodeSocket = null; - nodeInputStream = null; - nodeOutputStream = null; - nodeWriter = null; - nodeReader = null; - try { - nodeSocket = new Socket(node.getHostname(), node.getPort()); - nodeSocket.setReceiveBufferSize(65535); - nodeInputStream = nodeSocket.getInputStream(); - nodeOutputStream = nodeSocket.getOutputStream(); - nodeWriter = new OutputStreamWriter(nodeOutputStream, Charset.forName("UTF-8")); - nodeReader = new NodeReader(nodeInputStream); - Thread nodeReaderThread = new Thread(nodeReader); - nodeReaderThread.setDaemon(true); - nodeReaderThread.start(); - ClientHello clientHello = new ClientHello(); - clientHello.setName(name); - clientHello.setExpectedVersion("2.0"); - execute(clientHello); - synchronized (this) { - try { - wait(); - } catch (InterruptedException e) { - } - } - return nodeHello != null; - } catch (IOException ioe1) { - disconnect(); - throw ioe1; - } - } - - /** - * Returns whether this connection is still connected to the node. - * - * @return true if this connection is still valid, - * false otherwise - */ - public boolean isConnected() { - return (nodeHello != null) && (nodeSocket != null) && (nodeSocket.isConnected()); - } - - /** - * Returns the NodeHello message the node sent on connection. - * - * @return The NodeHello message of the node - */ - public Message getNodeHello() { - return nodeHello; - } - - /** - * Disconnects from the node. - */ - public void disconnect() { - Closer.close(nodeWriter); - nodeWriter = null; - Closer.close(nodeOutputStream); - nodeOutputStream = null; - Closer.close(nodeInputStream); - nodeInputStream = null; - nodeInputStream = null; - Closer.close(nodeSocket); - nodeSocket = null; - synchronized (this) { - notify(); - } - fireConnectionTerminated(); - } - - /** - * Executes the specified command. - * - * @param command - * The command to execute - * @throws IllegalStateException - * if the connection is not connected - * @throws IOException - * if an I/O error occurs - */ - public synchronized void execute(Command command) throws IllegalStateException, IOException { - execute(command, null); - } - - /** - * Executes the specified command. - * - * @param command - * The command to execute - * @param progressListener - * A progress listener for a payload transfer - * @throws IllegalStateException - * if the connection is not connected - * @throws IOException - * if an I/O error occurs - */ - public synchronized void execute(Command command, ProgressListener progressListener) throws IllegalStateException, IOException { - if (nodeSocket == null) { - throw new IllegalStateException("connection is not connected"); - } - nodeWriter.write(command.getCommandName() + Command.LINEFEED); - command.write(nodeWriter); - nodeWriter.write("EndMessage" + Command.LINEFEED); - nodeWriter.flush(); - if (command.hasPayload()) { - InputStream payloadInputStream = null; - try { - payloadInputStream = command.getPayload(); - StreamCopier.copy(payloadInputStream, nodeOutputStream, command.getPayloadLength(), progressListener); - } finally { - Closer.close(payloadInputStream); - } - nodeOutputStream.flush(); - } - } - - /** - * The reader thread for this connection. This is essentially a thread that - * reads lines from the node, creates messages from them and notifies - * listeners about the messages. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ - private class NodeReader implements Runnable { - - /** The input stream to read from. */ - @SuppressWarnings("hiding") - private InputStream nodeInputStream; - - /** - * Creates a new reader that reads from the specified input stream. - * - * @param nodeInputStream - * The input stream to read from - */ - public NodeReader(InputStream nodeInputStream) { - this.nodeInputStream = nodeInputStream; - } - - /** - * Main loop of the reader. Lines are read and converted into - * {@link Message} objects. - */ - @SuppressWarnings("synthetic-access") - public void run() { - LineInputStream nodeReader = null; - try { - nodeReader = new LineInputStream(nodeInputStream); - String line = ""; - Message message = null; - while (line != null) { - line = nodeReader.readLine(); - // System.err.println("> " + line); - if (line == null) { - break; - } - if (message == null) { - message = new Message(line); - continue; - } - if ("Data".equals(line)) { - /* need to read message from stream now */ - File tempFile = null; - try { - tempFile = File.createTempFile("fcpv2", "data", (tempDirectory != null) ? new File(tempDirectory) : null); - tempFile.deleteOnExit(); - FileOutputStream tempFileOutputStream = new FileOutputStream(tempFile); - long dataLength = Long.parseLong(message.get("DataLength")); - StreamCopier.copy(nodeInputStream, tempFileOutputStream, dataLength); - tempFileOutputStream.close(); - message.setPayloadInputStream(new TempFileInputStream(tempFile)); - } catch (IOException ioe1) { - ioe1.printStackTrace(); - } - } - if ("Data".equals(line) || "EndMessage".equals(line)) { - if (message.getName().equals("NodeHello")) { - nodeHello = message; - synchronized (Connection.this) { - Connection.this.notify(); - } - } else { - fireMessageReceived(message); - } - message = null; - continue; - } - int equalsPosition = line.indexOf('='); - if (equalsPosition > -1) { - String key = line.substring(0, equalsPosition).trim(); - String value = line.substring(equalsPosition + 1).trim(); - if (key.equals("Identifier")) { - message.setIdentifier(value); - } else { - message.put(key, value); - } - continue; - } - /* skip lines consisting of whitespace only */ - if (line.trim().length() == 0) { - continue; - } - /* if we got here, some error occured! */ - throw new IOException("Unexpected line: " + line); - } - } catch (IOException ioe1) { - // ioe1.printStackTrace(); - } finally { - if (nodeReader != null) { - try { - nodeReader.close(); - } catch (IOException ioe1) { - } - } - if (nodeInputStream != null) { - try { - nodeInputStream.close(); - } catch (IOException ioe1) { - } - } - } - Connection.this.disconnect(); - } - - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java b/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java deleted file mode 100644 index 8e55a2d..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/ConnectionListener.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * jSite - ConnectionListener.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.util.EventListener; - -/** - * Interface for clients that want to be notified when a message was received. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public interface ConnectionListener extends EventListener { - - /** - * Notifies a client that a message was received. - * - * @param connection - * The connection the message was received on - * @param message - * The message that was received - */ - public void messageReceived(Connection connection, Message message); - - /** - * Notifies a client that the connection to the node has been lost. - * - * @param connection - * The connection that was lost - */ - public void connectionTerminated(Connection connection); - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java deleted file mode 100644 index 9b12e74..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * jSite - DirectFileEntry.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; - -/** - * A {@link FileEntry} that sends its payload directly to the node, using the - * existing FCP connection. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class DirectFileEntry extends FileEntry { - - /** The input stream to read the data for this file from. */ - private final InputStream dataInputStream; - - /** The length of the data. */ - private final long dataLength; - - /** - * Creates a new FileEntry with the specified name and content type that - * gets its data from the specified byte array. - * - * @param filename - * The name of the file - * @param contentType - * The content type of the file - * @param dataBytes - * The content of the file - */ - public DirectFileEntry(String filename, String contentType, byte[] dataBytes) { - this(filename, contentType, new ByteArrayInputStream(dataBytes), dataBytes.length); - } - - /** - * Creates a new FileEntry with the specified name and content type that - * gets its data from the specified input stream. - * - * @param filename - * The name of the file - * @param contentType - * The content type of the file - * @param dataInputStream - * The input stream to read the content from - * @param dataLength - * The length of the data input stream - */ - public DirectFileEntry(String filename, String contentType, InputStream dataInputStream, long dataLength) { - super(filename, contentType); - this.dataInputStream = dataInputStream; - this.dataLength = dataLength; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return "direct"; - } - - /** - * Returns the input stream for the file's content. - * - * @return The input stream for the file's content - */ - public InputStream getDataInputStream() { - return dataInputStream; - } - - /** - * Returns the length of this file's content. - * - * @return The length of this file's content - */ - public long getDataLength() { - return dataLength; - } - -} \ No newline at end of file diff --git a/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java deleted file mode 100644 index bf036b4..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * jSite - DiskFileEntry.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * A {@link FileEntry} that reads the content from a file on the disk. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class DiskFileEntry extends FileEntry { - - /** The local file name. */ - private final String localFilename; - - /** - * Creates a new {@link FileEntry} with the specified name and content type - * that is read from the file specified by localFilename. - * - * @param filename - * The name of the file - * @param contentType - * The content type of the file - * @param localFilename - * The name of the local file that holds the content of the file - * to insert - */ - public DiskFileEntry(String filename, String contentType, String localFilename) { - super(filename, contentType); - this.localFilename = localFilename; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return "disk"; - } - - /** - * Returns the name of the local file that holds the content for this file. - * - * @return The name of the local file - */ - public String getLocalFilename() { - return localFilename; - } - -} \ No newline at end of file diff --git a/src/de/todesbaum/util/freenet/fcp2/FileEntry.java b/src/de/todesbaum/util/freenet/fcp2/FileEntry.java deleted file mode 100644 index 7adb11f..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/FileEntry.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * jSite - FileEntry.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * Abstract base class of file entries that are used in the - * {@link de.todesbaum.util.freenet.fcp2.ClientPutComplexDir} command to define - * the files of an insert. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public abstract class FileEntry { - - /** The name of the file. */ - private final String filename; - - /** The content type of the file. */ - private final String contentType; - - /** - * Creates a new file entry with the specified name and content type. The - * content type should be a standard MIME type with an additional charset - * specification for text-based types. - * - * @param filename - * The name of the file - * @param contentType - * The content type of the file, e.g. - * "application/x-tar" or - * "text/html; charset=iso8859-15" - */ - protected FileEntry(String filename, String contentType) { - this.filename = filename; - this.contentType = contentType; - } - - /** - * Returns the name of this entry's type. Can be one of direct, - * disk, or redirect. This method is - * implemented by the subclasses {@link DirectFileEntry}, - * {@link DiskFileEntry}, and {@link RedirectFileEntry}, respectively. - * - * @return The name of this entry's type - */ - public abstract String getName(); - - /** - * Returns the content type of this file. - * - * @return The content type of this file - */ - public String getContentType() { - return contentType; - } - - /** - * Returns the name of this file. - * - * @return The name of this file - */ - public String getFilename() { - return filename; - } - -} \ No newline at end of file diff --git a/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java b/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java deleted file mode 100644 index 84e4bf6..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/GenerateSSK.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * jSite - GenerateSSK.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * Implementation of the GenerateSSK command. - *

- * The node can answer with the following messages: SSKKeypair. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class GenerateSSK extends Command { - - /** - * Creates a new GenerateSSK request. - */ - public GenerateSSK() { - super("GenerateSSK", null); - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/Message.java b/src/de/todesbaum/util/freenet/fcp2/Message.java deleted file mode 100644 index 6c934ec..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/Message.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * jSite - Message.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/** - * Contains replies sent by the Freenet node. A message always has a name, and - * most of the messages also have an identifier which binds it to a specific - * command. Exceptions are among others NodeHello, - * SSKKeypair, and EndListPersistentRequests. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - * @see de.todesbaum.util.freenet.fcp2.Client - */ -public class Message { - - /** The name of this message. */ - private final String name; - - /** The identifier of this message. */ - private String identifier = ""; - - /** The parameters of this message. */ - private Map parameters = new HashMap(); - - /** The payload. */ - private InputStream payloadInputStream; - - /** - * Creates a new message with the specified name. - * - * @param name - * The name of this message - */ - public Message(String name) { - this.name = name; - } - - /** - * Returns the identifier of this message. - * - * @return The identifier - */ - public String getIdentifier() { - return identifier; - } - - /** - * Sets the identifier of this message. - * - * @param identifier - * The identifier of this message - */ - public void setIdentifier(String identifier) { - this.identifier = identifier; - } - - /** - * Returns the name of this message. - * - * @return The name of this message - */ - public String getName() { - return name; - } - - /** - * Tests whether this message contains the parameter with the specified key. - * Key names are compared ignoring case. - * - * @param key - * The name of the parameter - * @return true if this parameter exists in this message, - * false otherwise - */ - public boolean containsKey(String key) { - return parameters.containsKey(key.toLowerCase()); - } - - /** - * Returns all parameters of this message. The keys of the entries are all - * lower case so if you want to match the parameter names you have to watch - * out. - * - * @return All parameters of this message - */ - public Set> entrySet() { - return parameters.entrySet(); - } - - /** - * Returns the value of the parameter with the name specified by - * key. - * - * @param key - * The name of the parameter - * @return The value of the parameter - */ - public String get(String key) { - return parameters.get(key.toLowerCase()); - } - - /** - * Stores the specified value as parameter with the name specified by - * key. - * - * @param key - * The name of the parameter - * @param value - * The value of the parameter - * @return The previous value, or null if there was no - * previous value - */ - public String put(String key, String value) { - return parameters.put(key.toLowerCase(), value); - } - - /** - * Returns the number of parameters in this message. - * - * @return The number of parameters - */ - public int size() { - return parameters.size(); - } - - /** - * @return Returns the payloadInputStream. - */ - public InputStream getPayloadInputStream() { - return payloadInputStream; - } - - /** - * @param payloadInputStream - * The payloadInputStream to set. - */ - public void setPayloadInputStream(InputStream payloadInputStream) { - this.payloadInputStream = payloadInputStream; - } - - /** - * Returns a textual representation of this message, containing its name, - * the identifier, and the parameters. - * - * @return A textual representation of this message - */ - @Override - public String toString() { - return name + "[identifier=" + identifier + ",parameters=" + parameters.toString() + "]"; - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/Node.java b/src/de/todesbaum/util/freenet/fcp2/Node.java deleted file mode 100644 index d2fa91c..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/Node.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * jSite - Node.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * Contains the hostname and port number of the Freenet node. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class Node { - - /** The default port of FCPv2. */ - public static final int DEFAULT_PORT = 9481; - - /** The hostname of the node. */ - protected String hostname; - - /** The port number of the node. */ - protected int port; - - /** - * Creates a new node with the specified hostname and the default port - * number. - * - * @param hostname - * The hostname of the node - * @see #DEFAULT_PORT - */ - public Node(String hostname) { - this(hostname, DEFAULT_PORT); - } - - /** - * Creates a new node with the specified hostname and port number. - * - * @param hostname - * The hostname of the node - * @param port - * The port number of the node - */ - public Node(String hostname, int port) { - this.hostname = hostname; - this.port = port; - } - - /** - * Returns the hostname of the node. - * - * @return The hostname of the node - */ - public String getHostname() { - return hostname; - } - - /** - * Returns the port number of the node. - * - * @return The port number of the node - */ - public int getPort() { - return port; - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/Persistence.java b/src/de/todesbaum/util/freenet/fcp2/Persistence.java deleted file mode 100644 index 55e73f8..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/Persistence.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * jSite - Persistence.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * The possible persistence options. This specify whether (and for how long) the - * node remembers to execute a request and the results. Possible values are - * connection, reboot, and forever. - * connection means that a request is aborted as soon as the - * connection to the node is severed. reboot means that a request - * is remembered as long as the node is running but not after restarts. - * forever finally means that a request persists until it is - * explicitely deleted. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - * @see de.todesbaum.util.freenet.fcp2.ModifyPersistentRequest - * @see de.todesbaum.util.freenet.fcp2.RemovePersistentRequest - */ -public final class Persistence { - - /** - * Denotes that a request should be terminated if the connection to the node - * is severed. - */ - public static final Persistence CONNECTION = new Persistence("connection"); - - /** Denotes that a request should be remembered until the node is restarted. */ - public static final Persistence REBOOT = new Persistence("reboot"); - - /** - * Denotes that a request should be remembered until it is explicitely - * deleted. - */ - public static final Persistence FOREVER = new Persistence("forever"); - - /** The name of this persistence option. */ - private String name; - - /** - * Private constructor that creates a persistence option with the specified - * name. - * - * @param name - * The name of the persistence option. - */ - private Persistence(String name) { - this.name = name; - } - - /** - * Returns the name of this persistence option. - * - * @return The name of this persistence option - */ - public String getName() { - return name; - } - - /** - * Returns a textual representation of this persistence option. The result - * is identical to calling {@link #getName()}. - * - * @return The name of this persistence option - */ - @Override - public String toString() { - return name; - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java b/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java deleted file mode 100644 index 623bc5b..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/PriorityClass.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * jSite - PriorityClass.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * The possible priority classes. Possible values are, in order of descending - * priority: maximum (anything more important than fproxy), - * interactive (fproxy), semi-interactive (fproxy - * immediate mode large file downloads, not to disk), updatable - * (updatable site checks), bulk (large file downloads to disk), - * prefetch, minimum. - * - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public final class PriorityClass { - - /** Denotes maximum priority class. */ - public static final PriorityClass MAXIMUM = new PriorityClass("maximum", 0); - - /** Denotes interactive priority class. */ - public static final PriorityClass INTERACTIVE = new PriorityClass("interactive", 1); - - /** Denotes semi-interactive priority class. */ - public static final PriorityClass SEMI_INTERACTIVE = new PriorityClass("semiInteractive", 2); - - /** Denotes updatable priority class. */ - public static final PriorityClass UPDATABLE = new PriorityClass("updatable", 3); - - /** Denotes bulk priority class. */ - public static final PriorityClass BULK = new PriorityClass("bulk", 4); - - /** Denotes prefetch priority class. */ - public static final PriorityClass PREFETCH = new PriorityClass("prefetch", 5); - - /** Denotes minimum priority class. */ - public static final PriorityClass MINIMUM = new PriorityClass("minimum", 6); - - /** The name of the priority class. */ - private String name; - - /** The value of the priority class. */ - private int value; - - /** - * Creates a new priority class with the specified name and value. - * - * @param name - * The name of the priority class - * @param value - * The value of the priority class - */ - private PriorityClass(String name, int value) { - this.name = name; - this.value = value; - } - - /** - * Returns the name of this priority class. - * - * @return The name of this priority class - */ - public String getName() { - return name; - } - - /** - * Returns the value of this priority class. - * - * @return The value of this priority class - */ - public int getValue() { - return value; - } - - // - // STATIC METHODS - // - - /** - * Returns the priority class with the given name, matched case-insensitive. - * - * @param value - * The name of the priority - * @return The priority with the given name, or {@code null} if no priority - * matches the given name - */ - public static PriorityClass valueOf(String value) { - for (PriorityClass priorityClass : new PriorityClass[] { MINIMUM, PREFETCH, BULK, UPDATABLE, SEMI_INTERACTIVE, INTERACTIVE, MAXIMUM }) { - if (priorityClass.getName().equalsIgnoreCase(value)) { - return priorityClass; - } - } - return null; - } - - // - // OBJECT METHODS - // - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return name; - } - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java b/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java deleted file mode 100644 index 06a2171..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * jSite - RedirectFileEntry.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -public class RedirectFileEntry extends FileEntry { - - final String targetURI; - - public RedirectFileEntry(String filename, String contentType, String targetURI) { - super(filename, contentType); - this.targetURI = targetURI; - } - - /** - * {@inheritDoc} - */ - @Override - public String getName() { - return "redirect"; - } - - /** - * @return Returns the targetURI. - */ - public String getTargetURI() { - return targetURI; - } -} \ No newline at end of file diff --git a/src/de/todesbaum/util/freenet/fcp2/ReturnType.java b/src/de/todesbaum/util/freenet/fcp2/ReturnType.java deleted file mode 100644 index 6f2ed7a..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/ReturnType.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * jSite - ReturnType.java - Copyright © 2008–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * Enumeration for the different return types a {@link ClientGet} request can - * have. - * - * @author David ‘Bombe’ Roden <bombe@freenetproject.org> - */ -public enum ReturnType { - - /** The data is returned as payload. */ - direct, - - /** The data is written to disk. */ - disk, - - /** The data is not returned at all. */ - none - -} diff --git a/src/de/todesbaum/util/freenet/fcp2/Verbosity.java b/src/de/todesbaum/util/freenet/fcp2/Verbosity.java deleted file mode 100644 index e59b6a3..0000000 --- a/src/de/todesbaum/util/freenet/fcp2/Verbosity.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * jSite - Verbosity.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.freenet.fcp2; - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public final class Verbosity { - - public static final Verbosity PROGRESS = new Verbosity(1); - public static final Verbosity COMPRESSION = new Verbosity(512); - - public static final Verbosity NONE = new Verbosity(0); - public static final Verbosity ALL = new Verbosity(PROGRESS, COMPRESSION); - - private final int value; - - private Verbosity(int value) { - this.value = value; - } - - private Verbosity(Verbosity verbosity1, Verbosity verbosity2) { - this(verbosity1.value | verbosity2.value); - } - - /** - * @return Returns the value. - */ - public int getValue() { - return value; - } - -} diff --git a/src/de/todesbaum/util/image/IconLoader.java b/src/de/todesbaum/util/image/IconLoader.java deleted file mode 100644 index ff6441a..0000000 --- a/src/de/todesbaum/util/image/IconLoader.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * jSite - IconLoader.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.image; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import javax.swing.Icon; -import javax.swing.ImageIcon; - -/** - * @author David Roden - * @version $Id$ - */ -public class IconLoader { - - public static Icon loadIcon(String resourceName) { - try { - InputStream resourceStream = IconLoader.class.getResourceAsStream(resourceName); - if (resourceStream == null) { - return null; - } - ByteArrayOutputStream imageOutput = new ByteArrayOutputStream(); - byte[] buffer = new byte[16384]; - int r = 0; - while ((r = resourceStream.read(buffer)) != -1) { - imageOutput.write(buffer, 0, r); - } - imageOutput.flush(); - return new ImageIcon(imageOutput.toByteArray()); - } catch (IOException e) { - } - return null; - } - -} diff --git a/src/de/todesbaum/util/io/Closer.java b/src/de/todesbaum/util/io/Closer.java deleted file mode 100644 index 67f7c57..0000000 --- a/src/de/todesbaum/util/io/Closer.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * jSite - Closer.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.io; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; -import java.io.Writer; -import java.net.ServerSocket; -import java.net.Socket; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -/** - * Helper class that can close all kinds of resources without throwing exception - * so that clean-up code can be written with less code. All methods check that - * the given resource is not null before invoking the close() - * method of the respective type. - * - * @author David ‘Bombe&squo; - * Roden - * @version $Id$ - */ -public class Closer { - - /** - * Closes the given result set. - * - * @param resultSet - * The result set to close - * @see ResultSet#close() - */ - public static void close(ResultSet resultSet) { - if (resultSet != null) { - try { - resultSet.close(); - } catch (SQLException ioe1) { - } - } - } - - /** - * Closes the given statement. - * - * @param statement - * The statement to close - * @see Statement#close() - */ - public static void close(Statement statement) { - if (statement != null) { - try { - statement.close(); - } catch (SQLException ioe1) { - } - } - } - - /** - * Closes the given connection. - * - * @param connection - * The connection to close - * @see Connection#close() - */ - public static void close(Connection connection) { - if (connection != null) { - try { - connection.close(); - } catch (SQLException ioe1) { - } - } - } - - /** - * Closes the given server socket. - * - * @param serverSocket - * The server socket to close - * @see ServerSocket#close() - */ - public static void close(ServerSocket serverSocket) { - if (serverSocket != null) { - try { - serverSocket.close(); - } catch (IOException ioe1) { - } - } - } - - /** - * Closes the given socket. - * - * @param socket - * The socket to close - * @see Socket#close() - */ - public static void close(Socket socket) { - if (socket != null) { - try { - socket.close(); - } catch (IOException ioe1) { - } - } - } - - /** - * Closes the given input stream. - * - * @param inputStream - * The input stream to close - * @see InputStream#close() - */ - public static void close(InputStream inputStream) { - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException ioe1) { - } - } - } - - /** - * Closes the given output stream. - * - * @param outputStream - * The output stream to close - * @see OutputStream#close() - */ - public static void close(OutputStream outputStream) { - if (outputStream != null) { - try { - outputStream.close(); - } catch (IOException ioe1) { - } - } - } - - /** - * Closes the given reader. - * - * @param reader - * The reader to close - * @see Reader#close() - */ - public static void close(Reader reader) { - if (reader != null) { - try { - reader.close(); - } catch (IOException ioe1) { - } - } - } - - /** - * Closes the given writer. - * - * @param writer - * The write to close - * @see Writer#close() - */ - public static void close(Writer writer) { - if (writer != null) { - try { - writer.close(); - } catch (IOException ioe1) { - } - } - } - -} diff --git a/src/de/todesbaum/util/io/LineInputStream.java b/src/de/todesbaum/util/io/LineInputStream.java deleted file mode 100644 index 4fe0d07..0000000 --- a/src/de/todesbaum/util/io/LineInputStream.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * jSite - LineInputStream.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.io; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class LineInputStream extends FilterInputStream { - - private boolean skipLinefeed = false; - private StringBuffer lineBuffer = new StringBuffer(); - - /** - * @param in - */ - public LineInputStream(InputStream in) { - super(in); - } - - public synchronized String readLine() throws IOException { - lineBuffer.setLength(0); - int c = 0; - while (c != -1) { - c = read(); - if ((c == -1) && lineBuffer.length() == 0) - return null; - if (skipLinefeed && (c == '\n')) { - skipLinefeed = false; - continue; - } - skipLinefeed = (c == '\r'); - if ((c == '\r') || (c == '\n')) { - c = -1; - } else { - lineBuffer.append((char) c); - } - } - return lineBuffer.toString(); - } - -} diff --git a/src/de/todesbaum/util/io/StreamCopier.java b/src/de/todesbaum/util/io/StreamCopier.java deleted file mode 100644 index c7d99de..0000000 --- a/src/de/todesbaum/util/io/StreamCopier.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * jSite - StreamCopier.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.io; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.EventListener; - -/** - * Copies input from an {@link InputStream} to an {@link OutputStream}. - * - * @author David Roden - * @version $Id$ - */ -public class StreamCopier { - - /** - * The default size of the buffer. - */ - private static final int BUFFER_SIZE = 64 * 1024; - - /** - * The {@link InputStream} to read from. - */ - private InputStream inputStream; - - /** - * The {@link OutputStream} to write to. - */ - private OutputStream outputStream; - - /** - * The number of bytes to copy. - */ - private long length; - - /** - * The size of the buffer. - */ - private int bufferSize; - - /** - * Creates a new StreamCopier with the specified parameters and the default - * buffer size. - * - * @param inputStream - * The {@link InputStream} to read from - * @param outputStream - * The {@link OutputStream} to write to - * @param length - * The number of bytes to copy - */ - public StreamCopier(InputStream inputStream, OutputStream outputStream, long length) { - this(inputStream, outputStream, length, BUFFER_SIZE); - } - - /** - * Creates a new StreamCopier with the specified parameters and the default - * buffer size. - * - * @param inputStream - * The {@link InputStream} to read from - * @param outputStream - * The {@link OutputStream} to write to - * @param length - * The number of bytes to copy - * @param bufferSize - * The number of bytes to copy at a time - */ - public StreamCopier(InputStream inputStream, OutputStream outputStream, long length, int bufferSize) { - this.inputStream = inputStream; - this.outputStream = outputStream; - this.length = length; - this.bufferSize = bufferSize; - } - - /** - * Copies the stream data. If the input stream is depleted before the - * requested number of bytes have been read an {@link EOFException} is - * thrown. - * - * @throws EOFException - * if the input stream is depleted before the requested number - * of bytes has been read - * @throws IOException - * if an I/O error occurs - */ - public void copy() throws EOFException, IOException { - copy(inputStream, outputStream, length, bufferSize); - } - - /** - * Copies the stream data. If the input stream is depleted before the - * requested number of bytes have been read an {@link EOFException} is - * thrown. - * - * @param progressListener - * The progress listener (may be {@code null}) - * @throws EOFException - * if the input stream is depleted before the requested number - * of bytes has been read - * @throws IOException - * if an I/O error occurs - */ - public void copy(ProgressListener progressListener) throws EOFException, IOException { - copy(inputStream, outputStream, length, bufferSize, progressListener); - } - - /** - * Copies 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 - * @throws IOException - * if an I/O exception occurs - */ - public static void copy(InputStream inputStream, OutputStream outputStream, long length) throws IOException { - copy(inputStream, outputStream, length, BUFFER_SIZE); - } - - /** - * Copies 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 - * The input stream to read from - * @param outputStream - * The output stream to write to - * @param length - * The number of bytes to copy - * @param bufferSize - * The size of the copy buffer - * @throws IOException - * if an I/O exception occurs - */ - public static void copy(InputStream inputStream, OutputStream outputStream, long length, int bufferSize) throws IOException { - copy(inputStream, outputStream, length, bufferSize, null); - } - - /** - * Copies 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) { - int read = inputStream.read(buffer, 0, (int) Math.min(Integer.MAX_VALUE, Math.min(bufferSize, remaining))); - if (read == -1) { - throw new EOFException(); - } - outputStream.write(buffer, 0, read); - remaining -= read; - if (progressListener != null) { - progressListener.onProgress(length - remaining, length); - } - } - } - - /** - * Interface for objects that want to be notified about the progress of a - * {@link StreamCopier#copy()} operation. - * - * @author David ‘Bombe’ Roden <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 deleted file mode 100644 index 78860dc..0000000 --- a/src/de/todesbaum/util/io/TeeOutputStream.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * jSite - TeeOutputStream.java - Copyright © 2010 David Roden - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -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 deleted file mode 100644 index 43e2047..0000000 --- a/src/de/todesbaum/util/io/TempFileInputStream.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * jSite - TempFileInputStream.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.io; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class TempFileInputStream extends FileInputStream { - - private File tempFile; - - /** - * @param name - * @throws FileNotFoundException - */ - public TempFileInputStream(String name) throws FileNotFoundException { - this(new File(name)); - } - - /** - * @param file - * @throws FileNotFoundException - */ - public TempFileInputStream(File file) throws FileNotFoundException { - super(file); - tempFile = file; - } - - @Override - public void close() throws IOException { - super.close(); - tempFile.delete(); - } - -} diff --git a/src/de/todesbaum/util/mime/DefaultMIMETypes.java b/src/de/todesbaum/util/mime/DefaultMIMETypes.java deleted file mode 100644 index 9e380a5..0000000 --- a/src/de/todesbaum/util/mime/DefaultMIMETypes.java +++ /dev/null @@ -1,834 +0,0 @@ -/* taken from freenet (http://www.freenetproject.org/) */ -package de.todesbaum.util.mime; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Vector; - -/** - * Holds the default MIME types. - */ -public class DefaultMIMETypes { - - /** Default MIME type - what to set it to if we don't know any better */ - public static final String DEFAULT_MIME_TYPE = "application/octet-stream"; - - /** MIME types: number -> name */ - private static List mimeTypesByNumber = new Vector(); - - /** MIME types: name -> number */ - private static Map mimeTypesByName = new HashMap(); - - /** MIME types by extension. One extension maps to one MIME type, but not necessarily - * the other way around. */ - private static Map mimeTypesByExtension = new HashMap(); - - /** Primary extension by MIME type number. */ - private static Map primaryExtensionByMimeNumber = new HashMap(); - - /** - * Add a MIME type, without any extensions. - * @param number The number of the MIME type for compression. This *must not change* - * for a given type, or the metadata format will be affected. - * @param type The actual MIME type string. Do not include ;charset= etc; these are - * parameters and there is a separate mechanism for them. - */ - protected static synchronized void addMIMEType(short number, String type) { - if(mimeTypesByNumber.size() > number) { - String s = mimeTypesByNumber.get(number); - if(s != null) throw new IllegalArgumentException("Already used: "+number); - } else { - mimeTypesByNumber.add(number, null); - } - mimeTypesByNumber.set(number, type); - mimeTypesByName.put(type, new Short(number)); - } - - /** - * Add a MIME type. - * @param number The number of the MIME type for compression. This *must not change* - * for a given type, or the metadata format will be affected. - * @param type The actual MIME type string. Do not include ;charset= etc; these are - * parameters and there is a separate mechanism for them. - * @param extensions An array of common extensions for files of this type. Must be - * unique for the type. - */ - protected static synchronized void addMIMEType(short number, String type, String[] extensions, String outExtension) { - addMIMEType(number, type); - Short t = new Short(number); - if(extensions != null) { - for(int i=0;i mimeTypesByNumber.size() || x < 0) - return null; - return mimeTypesByNumber.get(x); - } - - /** - * Get the number of a MIME type, or -1 if it is not in the table of known MIME - * types, in which case it will have to be sent uncompressed. - */ - public static short byName(String s) { - Short x = mimeTypesByName.get(s); - if(x != null) return x.shortValue(); - return -1; - } - - /* From toad's /etc/mime.types - * cat /etc/mime.types | sed "/^$/d;/#/d" | tr --squeeze '\t' ' ' | - * (y=0; while read x; do echo "$x" | - * sed -n "s/^\([^ ]*\)$/addMIMEType\($y, \"\1\"\);/p;s/^\([^ (),]\+\) \(.*\)$/addMIMEType\($y, \"\1\", \"\2\"\);/p;"; y=$((y+1)); done) - */ - - static { - addMIMEType((short) 0, "application/activemessage"); - addMIMEType((short) 1, "application/andrew-inset", "ez"); - addMIMEType((short) 2, "application/applefile"); - addMIMEType((short) 3, "application/atomicmail"); - addMIMEType((short) 4, "application/batch-SMTP"); - addMIMEType((short) 5, "application/beep+xml"); - addMIMEType((short) 6, "application/cals-1840"); - addMIMEType((short) 7, "application/commonground"); - addMIMEType((short) 8, "application/cu-seeme", "cu"); - addMIMEType((short) 9, "application/cybercash"); - addMIMEType((short) 10, "application/dca-rft"); - addMIMEType((short) 11, "application/dec-dx"); - addMIMEType((short) 12, "application/docbook+xml"); - addMIMEType((short) 13, "application/dsptype", "tsp"); - addMIMEType((short) 14, "application/dvcs"); - addMIMEType((short) 15, "application/edi-consent"); - addMIMEType((short) 16, "application/edi-x12"); - addMIMEType((short) 17, "application/edifact"); - addMIMEType((short) 18, "application/eshop"); - addMIMEType((short) 19, "application/font-tdpfr"); - addMIMEType((short) 20, "application/futuresplash", "spl"); - addMIMEType((short) 21, "application/ghostview"); - addMIMEType((short) 22, "application/hta", "hta"); - addMIMEType((short) 23, "application/http"); - addMIMEType((short) 24, "application/hyperstudio"); - addMIMEType((short) 25, "application/iges"); - addMIMEType((short) 26, "application/index"); - addMIMEType((short) 27, "application/index.cmd"); - addMIMEType((short) 28, "application/index.obj"); - addMIMEType((short) 29, "application/index.response"); - addMIMEType((short) 30, "application/index.vnd"); - addMIMEType((short) 31, "application/iotp"); - addMIMEType((short) 32, "application/ipp"); - addMIMEType((short) 33, "application/isup"); - addMIMEType((short) 34, "application/java-archive", "jar"); - addMIMEType((short) 35, "application/java-serialized-object", "ser"); - addMIMEType((short) 36, "application/java-vm", "class"); - addMIMEType((short) 37, "application/mac-binhex40", "hqx"); - addMIMEType((short) 38, "application/mac-compactpro", "cpt"); - addMIMEType((short) 39, "application/macwriteii"); - addMIMEType((short) 40, "application/marc"); - addMIMEType((short) 41, "application/mathematica", "nb"); - addMIMEType((short) 42, "application/mathematica-old"); - addMIMEType((short) 43, "application/msaccess", "mdb"); - addMIMEType((short) 44, "application/msword", "doc dot"); - addMIMEType((short) 45, "application/news-message-id"); - addMIMEType((short) 46, "application/news-transmission"); - addMIMEType((short) 47, "application/ocsp-request"); - addMIMEType((short) 48, "application/ocsp-response"); - addMIMEType((short) 49, "application/octet-stream", "bin"); - addMIMEType((short) 50, "application/oda", "oda"); - addMIMEType((short) 51, "application/ogg", "ogg"); - addMIMEType((short) 52, "application/parityfec"); - addMIMEType((short) 53, "application/pdf", "pdf"); - addMIMEType((short) 54, "application/pgp-encrypted"); - addMIMEType((short) 55, "application/pgp-keys", "key"); - addMIMEType((short) 56, "application/pgp-signature", "pgp"); - addMIMEType((short) 57, "application/pics-rules", "prf"); - addMIMEType((short) 58, "application/pkcs10"); - addMIMEType((short) 59, "application/pkcs7-mime"); - addMIMEType((short) 60, "application/pkcs7-signature"); - addMIMEType((short) 61, "application/pkix-cert"); - addMIMEType((short) 62, "application/pkix-crl"); - addMIMEType((short) 63, "application/pkixcmp"); - addMIMEType((short) 64, "application/postscript", "ps ai eps"); - addMIMEType((short) 65, "application/prs.alvestrand.titrax-sheet"); - addMIMEType((short) 66, "application/prs.cww"); - addMIMEType((short) 67, "application/prs.nprend"); - addMIMEType((short) 68, "application/qsig"); - addMIMEType((short) 69, "application/rar", "rar"); - addMIMEType((short) 70, "application/rdf+xml", "rdf"); - addMIMEType((short) 71, "application/remote-printing"); - addMIMEType((short) 72, "application/riscos"); - addMIMEType((short) 73, "application/rss+xml", "rss"); - addMIMEType((short) 74, "application/rtf"); - addMIMEType((short) 75, "application/sdp"); - addMIMEType((short) 76, "application/set-payment"); - addMIMEType((short) 77, "application/set-payment-initiation"); - addMIMEType((short) 78, "application/set-registration"); - addMIMEType((short) 79, "application/set-registration-initiation"); - addMIMEType((short) 80, "application/sgml"); - addMIMEType((short) 81, "application/sgml-open-catalog"); - addMIMEType((short) 82, "application/sieve"); - addMIMEType((short) 83, "application/slate"); - addMIMEType((short) 84, "application/smil", "smi smil"); - addMIMEType((short) 85, "application/timestamp-query"); - addMIMEType((short) 86, "application/timestamp-reply"); - addMIMEType((short) 87, "application/vemmi"); - addMIMEType((short) 88, "application/whoispp-query"); - addMIMEType((short) 89, "application/whoispp-response"); - addMIMEType((short) 90, "application/wita"); - addMIMEType((short) 91, "application/wordperfect", "wpd"); - addMIMEType((short) 92, "application/wordperfect5.1", "wp5"); - addMIMEType((short) 93, "application/x400-bp"); - addMIMEType((short) 94, "application/xhtml+xml", "xhtml xht"); - addMIMEType((short) 95, "application/xml", "xml xsl"); - addMIMEType((short) 96, "application/xml-dtd"); - addMIMEType((short) 97, "application/xml-external-parsed-entity"); - addMIMEType((short) 98, "application/zip", "zip"); - addMIMEType((short) 99, "application/vnd.3M.Post-it-Notes"); - addMIMEType((short) 100, "application/vnd.accpac.simply.aso"); - addMIMEType((short) 101, "application/vnd.accpac.simply.imp"); - addMIMEType((short) 102, "application/vnd.acucobol"); - addMIMEType((short) 103, "application/vnd.aether.imp"); - addMIMEType((short) 104, "application/vnd.anser-web-certificate-issue-initiation"); - addMIMEType((short) 105, "application/vnd.anser-web-funds-transfer-initiation"); - addMIMEType((short) 106, "application/vnd.audiograph"); - addMIMEType((short) 107, "application/vnd.bmi"); - addMIMEType((short) 108, "application/vnd.businessobjects"); - addMIMEType((short) 109, "application/vnd.canon-cpdl"); - addMIMEType((short) 110, "application/vnd.canon-lips"); - addMIMEType((short) 111, "application/vnd.cinderella", "cdy"); - addMIMEType((short) 112, "application/vnd.claymore"); - addMIMEType((short) 113, "application/vnd.commerce-battelle"); - addMIMEType((short) 114, "application/vnd.commonspace"); - addMIMEType((short) 115, "application/vnd.comsocaller"); - addMIMEType((short) 116, "application/vnd.contact.cmsg"); - addMIMEType((short) 117, "application/vnd.cosmocaller"); - addMIMEType((short) 118, "application/vnd.ctc-posml"); - addMIMEType((short) 119, "application/vnd.cups-postscript"); - addMIMEType((short) 120, "application/vnd.cups-raster"); - addMIMEType((short) 121, "application/vnd.cups-raw"); - addMIMEType((short) 122, "application/vnd.cybank"); - addMIMEType((short) 123, "application/vnd.dna"); - addMIMEType((short) 124, "application/vnd.dpgraph"); - addMIMEType((short) 125, "application/vnd.dxr"); - addMIMEType((short) 126, "application/vnd.ecdis-update"); - addMIMEType((short) 127, "application/vnd.ecowin.chart"); - addMIMEType((short) 128, "application/vnd.ecowin.filerequest"); - addMIMEType((short) 129, "application/vnd.ecowin.fileupdate"); - addMIMEType((short) 130, "application/vnd.ecowin.series"); - addMIMEType((short) 131, "application/vnd.ecowin.seriesrequest"); - addMIMEType((short) 132, "application/vnd.ecowin.seriesupdate"); - addMIMEType((short) 133, "application/vnd.enliven"); - addMIMEType((short) 134, "application/vnd.epson.esf"); - addMIMEType((short) 135, "application/vnd.epson.msf"); - addMIMEType((short) 136, "application/vnd.epson.quickanime"); - addMIMEType((short) 137, "application/vnd.epson.salt"); - addMIMEType((short) 138, "application/vnd.epson.ssf"); - addMIMEType((short) 139, "application/vnd.ericsson.quickcall"); - addMIMEType((short) 140, "application/vnd.eudora.data"); - addMIMEType((short) 141, "application/vnd.fdf"); - addMIMEType((short) 142, "application/vnd.ffsns"); - addMIMEType((short) 143, "application/vnd.flographit"); - addMIMEType((short) 144, "application/vnd.framemaker"); - addMIMEType((short) 145, "application/vnd.fsc.weblaunch"); - addMIMEType((short) 146, "application/vnd.fujitsu.oasys"); - addMIMEType((short) 147, "application/vnd.fujitsu.oasys2"); - addMIMEType((short) 148, "application/vnd.fujitsu.oasys3"); - addMIMEType((short) 149, "application/vnd.fujitsu.oasysgp"); - addMIMEType((short) 150, "application/vnd.fujitsu.oasysprs"); - addMIMEType((short) 151, "application/vnd.fujixerox.ddd"); - addMIMEType((short) 152, "application/vnd.fujixerox.docuworks"); - addMIMEType((short) 153, "application/vnd.fujixerox.docuworks.binder"); - addMIMEType((short) 154, "application/vnd.fut-misnet"); - addMIMEType((short) 155, "application/vnd.grafeq"); - addMIMEType((short) 156, "application/vnd.groove-account"); - addMIMEType((short) 157, "application/vnd.groove-identity-message"); - addMIMEType((short) 158, "application/vnd.groove-injector"); - addMIMEType((short) 159, "application/vnd.groove-tool-message"); - addMIMEType((short) 160, "application/vnd.groove-tool-template"); - addMIMEType((short) 161, "application/vnd.groove-vcard"); - addMIMEType((short) 162, "application/vnd.hhe.lesson-player"); - addMIMEType((short) 163, "application/vnd.hp-HPGL"); - addMIMEType((short) 164, "application/vnd.hp-PCL"); - addMIMEType((short) 165, "application/vnd.hp-PCLXL"); - addMIMEType((short) 166, "application/vnd.hp-hpid"); - addMIMEType((short) 167, "application/vnd.hp-hps"); - addMIMEType((short) 168, "application/vnd.httphone"); - addMIMEType((short) 169, "application/vnd.hzn-3d-crossword"); - addMIMEType((short) 170, "application/vnd.ibm.MiniPay"); - addMIMEType((short) 171, "application/vnd.ibm.afplinedata"); - addMIMEType((short) 172, "application/vnd.ibm.modcap"); - addMIMEType((short) 173, "application/vnd.informix-visionary"); - addMIMEType((short) 174, "application/vnd.intercon.formnet"); - addMIMEType((short) 175, "application/vnd.intertrust.digibox"); - addMIMEType((short) 176, "application/vnd.intertrust.nncp"); - addMIMEType((short) 177, "application/vnd.intu.qbo"); - addMIMEType((short) 178, "application/vnd.intu.qfx"); - addMIMEType((short) 179, "application/vnd.irepository.package+xml"); - addMIMEType((short) 180, "application/vnd.is-xpr"); - addMIMEType((short) 181, "application/vnd.japannet-directory-service"); - addMIMEType((short) 182, "application/vnd.japannet-jpnstore-wakeup"); - addMIMEType((short) 183, "application/vnd.japannet-payment-wakeup"); - addMIMEType((short) 184, "application/vnd.japannet-registration"); - addMIMEType((short) 185, "application/vnd.japannet-registration-wakeup"); - addMIMEType((short) 186, "application/vnd.japannet-setstore-wakeup"); - addMIMEType((short) 187, "application/vnd.japannet-verification"); - addMIMEType((short) 188, "application/vnd.japannet-verification-wakeup"); - addMIMEType((short) 189, "application/vnd.koan"); - addMIMEType((short) 190, "application/vnd.lotus-1-2-3"); - addMIMEType((short) 191, "application/vnd.lotus-approach"); - addMIMEType((short) 192, "application/vnd.lotus-freelance"); - addMIMEType((short) 193, "application/vnd.lotus-notes"); - addMIMEType((short) 194, "application/vnd.lotus-organizer"); - addMIMEType((short) 195, "application/vnd.lotus-screencam"); - addMIMEType((short) 196, "application/vnd.lotus-wordpro"); - addMIMEType((short) 197, "application/vnd.mcd"); - addMIMEType((short) 198, "application/vnd.mediastation.cdkey"); - addMIMEType((short) 199, "application/vnd.meridian-slingshot"); - addMIMEType((short) 200, "application/vnd.mif"); - addMIMEType((short) 201, "application/vnd.minisoft-hp3000-save"); - addMIMEType((short) 202, "application/vnd.mitsubishi.misty-guard.trustweb"); - addMIMEType((short) 203, "application/vnd.mobius.daf"); - addMIMEType((short) 204, "application/vnd.mobius.dis"); - addMIMEType((short) 205, "application/vnd.mobius.msl"); - addMIMEType((short) 206, "application/vnd.mobius.plc"); - addMIMEType((short) 207, "application/vnd.mobius.txf"); - addMIMEType((short) 208, "application/vnd.motorola.flexsuite"); - addMIMEType((short) 209, "application/vnd.motorola.flexsuite.adsi"); - addMIMEType((short) 210, "application/vnd.motorola.flexsuite.fis"); - addMIMEType((short) 211, "application/vnd.motorola.flexsuite.gotap"); - addMIMEType((short) 212, "application/vnd.motorola.flexsuite.kmr"); - addMIMEType((short) 213, "application/vnd.motorola.flexsuite.ttc"); - addMIMEType((short) 214, "application/vnd.motorola.flexsuite.wem"); - addMIMEType((short) 215, "application/vnd.mozilla.xul+xml", "xul"); - addMIMEType((short) 216, "application/vnd.ms-artgalry"); - addMIMEType((short) 217, "application/vnd.ms-asf"); - addMIMEType((short) 218, "application/vnd.ms-excel", "xls xlb xlt"); - addMIMEType((short) 219, "application/vnd.ms-lrm"); - addMIMEType((short) 220, "application/vnd.ms-pki.seccat", "cat"); - addMIMEType((short) 221, "application/vnd.ms-pki.stl", "stl"); - addMIMEType((short) 222, "application/vnd.ms-powerpoint", "ppt pps"); - addMIMEType((short) 223, "application/vnd.ms-project"); - addMIMEType((short) 224, "application/vnd.ms-tnef"); - addMIMEType((short) 225, "application/vnd.ms-works"); - addMIMEType((short) 226, "application/vnd.mseq"); - addMIMEType((short) 227, "application/vnd.msign"); - addMIMEType((short) 228, "application/vnd.music-niff"); - addMIMEType((short) 229, "application/vnd.musician"); - addMIMEType((short) 230, "application/vnd.netfpx"); - addMIMEType((short) 231, "application/vnd.noblenet-directory"); - addMIMEType((short) 232, "application/vnd.noblenet-sealer"); - addMIMEType((short) 233, "application/vnd.noblenet-web"); - addMIMEType((short) 234, "application/vnd.novadigm.EDM"); - addMIMEType((short) 235, "application/vnd.novadigm.EDX"); - addMIMEType((short) 236, "application/vnd.novadigm.EXT"); - addMIMEType((short) 237, "application/vnd.oasis.opendocument.chart", "odc"); - addMIMEType((short) 238, "application/vnd.oasis.opendocument.database", "odb"); - addMIMEType((short) 239, "application/vnd.oasis.opendocument.formula", "odf"); - addMIMEType((short) 240, "application/vnd.oasis.opendocument.graphics", "odg"); - addMIMEType((short) 241, "application/vnd.oasis.opendocument.graphics-template", "otg"); - addMIMEType((short) 242, "application/vnd.oasis.opendocument.image", "odi"); - addMIMEType((short) 243, "application/vnd.oasis.opendocument.presentation", "odp"); - addMIMEType((short) 244, "application/vnd.oasis.opendocument.presentation-template", "otp"); - addMIMEType((short) 245, "application/vnd.oasis.opendocument.spreadsheet", "ods"); - addMIMEType((short) 246, "application/vnd.oasis.opendocument.spreadsheet-template", "ots"); - addMIMEType((short) 247, "application/vnd.oasis.opendocument.text", "odt"); - addMIMEType((short) 248, "application/vnd.oasis.opendocument.text-master", "odm"); - addMIMEType((short) 249, "application/vnd.oasis.opendocument.text-template", "ott"); - addMIMEType((short) 250, "application/vnd.oasis.opendocument.text-web", "oth"); - addMIMEType((short) 251, "application/vnd.osa.netdeploy"); - addMIMEType((short) 252, "application/vnd.palm"); - addMIMEType((short) 253, "application/vnd.pg.format"); - addMIMEType((short) 254, "application/vnd.pg.osasli"); - addMIMEType((short) 255, "application/vnd.powerbuilder6"); - addMIMEType((short) 256, "application/vnd.powerbuilder6-s"); - addMIMEType((short) 257, "application/vnd.powerbuilder7"); - addMIMEType((short) 258, "application/vnd.powerbuilder7-s"); - addMIMEType((short) 259, "application/vnd.powerbuilder75"); - addMIMEType((short) 260, "application/vnd.powerbuilder75-s"); - addMIMEType((short) 261, "application/vnd.previewsystems.box"); - addMIMEType((short) 262, "application/vnd.publishare-delta-tree"); - addMIMEType((short) 263, "application/vnd.pvi.ptid1"); - addMIMEType((short) 264, "application/vnd.pwg-xhtml-print+xml"); - addMIMEType((short) 265, "application/vnd.rapid"); - addMIMEType((short) 266, "application/vnd.rim.cod", "cod"); - addMIMEType((short) 267, "application/vnd.s3sms"); - addMIMEType((short) 268, "application/vnd.seemail"); - addMIMEType((short) 269, "application/vnd.shana.informed.formdata"); - addMIMEType((short) 270, "application/vnd.shana.informed.formtemplate"); - addMIMEType((short) 271, "application/vnd.shana.informed.interchange"); - addMIMEType((short) 272, "application/vnd.shana.informed.package"); - addMIMEType((short) 273, "application/vnd.smaf", "mmf"); - addMIMEType((short) 274, "application/vnd.sss-cod"); - addMIMEType((short) 275, "application/vnd.sss-dtf"); - addMIMEType((short) 276, "application/vnd.sss-ntf"); - addMIMEType((short) 277, "application/vnd.stardivision.calc", "sdc"); - addMIMEType((short) 278, "application/vnd.stardivision.draw", "sda"); - addMIMEType((short) 279, "application/vnd.stardivision.impress", "sdd sdp"); - addMIMEType((short) 280, "application/vnd.stardivision.math", "smf"); - addMIMEType((short) 281, "application/vnd.stardivision.writer", "sdw vor"); - addMIMEType((short) 282, "application/vnd.stardivision.writer-global", "sgl"); - addMIMEType((short) 283, "application/vnd.street-stream"); - addMIMEType((short) 284, "application/vnd.sun.xml.calc", "sxc"); - addMIMEType((short) 285, "application/vnd.sun.xml.calc.template", "stc"); - addMIMEType((short) 286, "application/vnd.sun.xml.draw", "sxd"); - addMIMEType((short) 287, "application/vnd.sun.xml.draw.template", "std"); - addMIMEType((short) 288, "application/vnd.sun.xml.impress", "sxi"); - addMIMEType((short) 289, "application/vnd.sun.xml.impress.template", "sti"); - addMIMEType((short) 290, "application/vnd.sun.xml.math", "sxm"); - addMIMEType((short) 291, "application/vnd.sun.xml.writer", "sxw"); - addMIMEType((short) 292, "application/vnd.sun.xml.writer.global", "sxg"); - addMIMEType((short) 293, "application/vnd.sun.xml.writer.template", "stw"); - addMIMEType((short) 294, "application/vnd.svd"); - addMIMEType((short) 295, "application/vnd.swiftview-ics"); - addMIMEType((short) 296, "application/vnd.symbian.install", "sis"); - addMIMEType((short) 297, "application/vnd.triscape.mxs"); - addMIMEType((short) 298, "application/vnd.trueapp"); - addMIMEType((short) 299, "application/vnd.truedoc"); - addMIMEType((short) 300, "application/vnd.tve-trigger"); - addMIMEType((short) 301, "application/vnd.ufdl"); - addMIMEType((short) 302, "application/vnd.uplanet.alert"); - addMIMEType((short) 303, "application/vnd.uplanet.alert-wbxml"); - addMIMEType((short) 304, "application/vnd.uplanet.bearer-choice"); - addMIMEType((short) 305, "application/vnd.uplanet.bearer-choice-wbxml"); - addMIMEType((short) 306, "application/vnd.uplanet.cacheop"); - addMIMEType((short) 307, "application/vnd.uplanet.cacheop-wbxml"); - addMIMEType((short) 308, "application/vnd.uplanet.channel"); - addMIMEType((short) 309, "application/vnd.uplanet.channel-wbxml"); - addMIMEType((short) 310, "application/vnd.uplanet.list"); - addMIMEType((short) 311, "application/vnd.uplanet.list-wbxml"); - addMIMEType((short) 312, "application/vnd.uplanet.listcmd"); - addMIMEType((short) 313, "application/vnd.uplanet.listcmd-wbxml"); - addMIMEType((short) 314, "application/vnd.uplanet.signal"); - addMIMEType((short) 315, "application/vnd.vcx"); - addMIMEType((short) 316, "application/vnd.vectorworks"); - addMIMEType((short) 317, "application/vnd.vidsoft.vidconference"); - addMIMEType((short) 318, "application/vnd.visio", "vsd"); - addMIMEType((short) 319, "application/vnd.vividence.scriptfile"); - addMIMEType((short) 320, "application/vnd.wap.sic"); - addMIMEType((short) 321, "application/vnd.wap.slc"); - addMIMEType((short) 322, "application/vnd.wap.wbxml", "wbxml"); - addMIMEType((short) 323, "application/vnd.wap.wmlc", "wmlc"); - addMIMEType((short) 324, "application/vnd.wap.wmlscriptc", "wmlsc"); - addMIMEType((short) 325, "application/vnd.webturbo"); - addMIMEType((short) 326, "application/vnd.wrq-hp3000-labelled"); - addMIMEType((short) 327, "application/vnd.wt.stf"); - addMIMEType((short) 328, "application/vnd.xara"); - addMIMEType((short) 329, "application/vnd.xfdl"); - addMIMEType((short) 330, "application/vnd.yellowriver-custom-menu"); - addMIMEType((short) 331, "application/x-123", "wk"); - addMIMEType((short) 332, "application/x-abiword", "abw"); - addMIMEType((short) 333, "application/x-apple-diskimage", "dmg"); - addMIMEType((short) 334, "application/x-bcpio", "bcpio"); - addMIMEType((short) 335, "application/x-bittorrent", "torrent"); - addMIMEType((short) 336, "application/x-cdf", "cdf"); - addMIMEType((short) 337, "application/x-cdlink", "vcd"); - addMIMEType((short) 338, "application/x-chess-pgn", "pgn"); - addMIMEType((short) 339, "application/x-core"); - addMIMEType((short) 340, "application/x-cpio", "cpio"); - addMIMEType((short) 341, "application/x-csh", "csh"); - addMIMEType((short) 342, "application/x-debian-package", "deb udeb"); - addMIMEType((short) 343, "application/x-director", "dcr dir dxr"); - addMIMEType((short) 344, "application/x-dms", "dms"); - addMIMEType((short) 345, "application/x-doom", "wad"); - addMIMEType((short) 346, "application/x-dvi", "dvi"); - addMIMEType((short) 347, "application/x-executable"); - addMIMEType((short) 348, "application/x-flac", "flac"); - addMIMEType((short) 349, "application/x-font", "pfa pfb gsf pcf pcf.Z"); - addMIMEType((short) 350, "application/x-freemind", "mm"); - addMIMEType((short) 351, "application/x-futuresplash", "spl"); - addMIMEType((short) 352, "application/x-gnumeric", "gnumeric"); - addMIMEType((short) 353, "application/x-go-sgf", "sgf"); - addMIMEType((short) 354, "application/x-graphing-calculator", "gcf"); - addMIMEType((short) 355, "application/x-gtar", "gtar tgz taz"); - addMIMEType((short) 356, "application/x-hdf", "hdf"); - addMIMEType((short) 357, "application/x-httpd-php", "phtml pht php"); - addMIMEType((short) 358, "application/x-httpd-php-source", "phps"); - addMIMEType((short) 359, "application/x-httpd-php3", "php3"); - addMIMEType((short) 360, "application/x-httpd-php3-preprocessed", "php3p"); - addMIMEType((short) 361, "application/x-httpd-php4", "php4"); - addMIMEType((short) 362, "application/x-ica", "ica"); - addMIMEType((short) 363, "application/x-internet-signup", "ins isp"); - addMIMEType((short) 364, "application/x-iphone", "iii"); - addMIMEType((short) 365, "application/x-iso9660-image", "iso"); - addMIMEType((short) 366, "application/x-java-applet"); - addMIMEType((short) 367, "application/x-java-bean"); - addMIMEType((short) 368, "application/x-java-jnlp-file", "jnlp"); - addMIMEType((short) 369, "application/x-javascript", "js"); - addMIMEType((short) 370, "application/x-jmol", "jmz"); - addMIMEType((short) 371, "application/x-kchart", "chrt"); - addMIMEType((short) 372, "application/x-kdelnk"); - addMIMEType((short) 373, "application/x-killustrator", "kil"); - addMIMEType((short) 374, "application/x-koan", "skp skd skt skm"); - addMIMEType((short) 375, "application/x-kpresenter", "kpr kpt"); - addMIMEType((short) 376, "application/x-kspread", "ksp"); - addMIMEType((short) 377, "application/x-kword", "kwd kwt"); - addMIMEType((short) 378, "application/x-latex", "latex"); - addMIMEType((short) 379, "application/x-lha", "lha"); - addMIMEType((short) 380, "application/x-lzh", "lzh"); - addMIMEType((short) 381, "application/x-lzx", "lzx"); - addMIMEType((short) 382, "application/x-maker", "frm maker frame fm fb book fbdoc"); - addMIMEType((short) 383, "application/x-mif", "mif"); - addMIMEType((short) 384, "application/x-ms-wmd", "wmd"); - addMIMEType((short) 385, "application/x-ms-wmz", "wmz"); - addMIMEType((short) 386, "application/x-msdos-program", "com exe bat dll"); - addMIMEType((short) 387, "application/x-msi", "msi"); - addMIMEType((short) 388, "application/x-netcdf", "nc"); - addMIMEType((short) 389, "application/x-ns-proxy-autoconfig", "pac"); - addMIMEType((short) 390, "application/x-nwc", "nwc"); - addMIMEType((short) 391, "application/x-object", "o"); - addMIMEType((short) 392, "application/x-oz-application", "oza"); - addMIMEType((short) 393, "application/x-pkcs7-certreqresp", "p7r"); - addMIMEType((short) 394, "application/x-pkcs7-crl", "crl"); - addMIMEType((short) 395, "application/x-python-code", "pyc pyo"); - addMIMEType((short) 396, "application/x-quicktimeplayer", "qtl"); - addMIMEType((short) 397, "application/x-redhat-package-manager", "rpm"); - addMIMEType((short) 398, "application/x-rx"); - addMIMEType((short) 399, "application/x-sh", "sh"); - addMIMEType((short) 400, "application/x-shar", "shar"); - addMIMEType((short) 401, "application/x-shellscript"); - addMIMEType((short) 402, "application/x-shockwave-flash", "swf swfl"); - addMIMEType((short) 403, "application/x-stuffit", "sit"); - addMIMEType((short) 404, "application/x-sv4cpio", "sv4cpio"); - addMIMEType((short) 405, "application/x-sv4crc", "sv4crc"); - addMIMEType((short) 406, "application/x-tar", "tar"); - addMIMEType((short) 407, "application/x-tcl", "tcl"); - addMIMEType((short) 408, "application/x-tex-gf", "gf"); - addMIMEType((short) 409, "application/x-tex-pk", "pk"); - addMIMEType((short) 410, "application/x-texinfo", "texinfo texi"); - addMIMEType((short) 411, "application/x-trash", "~ % bak old sik"); - addMIMEType((short) 412, "application/x-troff", "t tr roff"); - addMIMEType((short) 413, "application/x-troff-man", "man"); - addMIMEType((short) 414, "application/x-troff-me", "me"); - addMIMEType((short) 415, "application/x-troff-ms", "ms"); - addMIMEType((short) 416, "application/x-ustar", "ustar"); - addMIMEType((short) 417, "application/x-videolan"); - addMIMEType((short) 418, "application/x-wais-source", "src"); - addMIMEType((short) 419, "application/x-wingz", "wz"); - addMIMEType((short) 420, "application/x-x509-ca-cert", "crt"); - addMIMEType((short) 421, "application/x-xcf", "xcf"); - addMIMEType((short) 422, "application/x-xfig", "fig"); - addMIMEType((short) 423, "application/x-xpinstall", "xpi"); - addMIMEType((short) 424, "audio/32kadpcm"); - addMIMEType((short) 425, "audio/basic", "au snd"); - addMIMEType((short) 426, "audio/g.722.1"); - addMIMEType((short) 427, "audio/l16"); - addMIMEType((short) 428, "audio/midi", "mid midi kar"); - addMIMEType((short) 429, "audio/mp4a-latm"); - addMIMEType((short) 430, "audio/mpa-robust"); - addMIMEType((short) 431, "audio/mpeg", "mpga mpega mp2 mp3 m4a"); - addMIMEType((short) 432, "audio/mpegurl", "m3u"); - addMIMEType((short) 433, "audio/parityfec"); - addMIMEType((short) 434, "audio/prs.sid", "sid"); - addMIMEType((short) 435, "audio/telephone-event"); - addMIMEType((short) 436, "audio/tone"); - addMIMEType((short) 437, "audio/vnd.cisco.nse"); - addMIMEType((short) 438, "audio/vnd.cns.anp1"); - addMIMEType((short) 439, "audio/vnd.cns.inf1"); - addMIMEType((short) 440, "audio/vnd.digital-winds"); - addMIMEType((short) 441, "audio/vnd.everad.plj"); - addMIMEType((short) 442, "audio/vnd.lucent.voice"); - addMIMEType((short) 443, "audio/vnd.nortel.vbk"); - addMIMEType((short) 444, "audio/vnd.nuera.ecelp4800"); - addMIMEType((short) 445, "audio/vnd.nuera.ecelp7470"); - addMIMEType((short) 446, "audio/vnd.nuera.ecelp9600"); - addMIMEType((short) 447, "audio/vnd.octel.sbc"); - addMIMEType((short) 448, "audio/vnd.qcelp"); - addMIMEType((short) 449, "audio/vnd.rhetorex.32kadpcm"); - addMIMEType((short) 450, "audio/vnd.vmx.cvsd"); - addMIMEType((short) 451, "audio/x-aiff", "aif aiff aifc"); - addMIMEType((short) 452, "audio/x-gsm", "gsm"); - addMIMEType((short) 453, "audio/x-mpegurl", "m3u"); - addMIMEType((short) 454, "audio/x-ms-wma", "wma"); - addMIMEType((short) 455, "audio/x-ms-wax", "wax"); - addMIMEType((short) 456, "audio/x-pn-realaudio-plugin"); - addMIMEType((short) 457, "audio/x-pn-realaudio", "ra rm ram"); - addMIMEType((short) 458, "audio/x-realaudio", "ra"); - addMIMEType((short) 459, "audio/x-scpls", "pls"); - addMIMEType((short) 460, "audio/x-sd2", "sd2"); - addMIMEType((short) 461, "audio/x-wav", "wav"); - addMIMEType((short) 462, "chemical/x-alchemy", "alc"); - addMIMEType((short) 463, "chemical/x-cache", "cac cache"); - addMIMEType((short) 464, "chemical/x-cache-csf", "csf"); - addMIMEType((short) 465, "chemical/x-cactvs-binary", "cbin cascii ctab"); - addMIMEType((short) 466, "chemical/x-cdx", "cdx"); - addMIMEType((short) 467, "chemical/x-cerius", "cer"); - addMIMEType((short) 468, "chemical/x-chem3d", "c3d"); - addMIMEType((short) 469, "chemical/x-chemdraw", "chm"); - addMIMEType((short) 470, "chemical/x-cif", "cif"); - addMIMEType((short) 471, "chemical/x-cmdf", "cmdf"); - addMIMEType((short) 472, "chemical/x-cml", "cml"); - addMIMEType((short) 473, "chemical/x-compass", "cpa"); - addMIMEType((short) 474, "chemical/x-crossfire", "bsd"); - addMIMEType((short) 475, "chemical/x-csml", "csml csm"); - addMIMEType((short) 476, "chemical/x-ctx", "ctx"); - addMIMEType((short) 477, "chemical/x-cxf", "cxf cef"); - addMIMEType((short) 478, "chemical/x-embl-dl-nucleotide", "emb embl"); - addMIMEType((short) 479, "chemical/x-galactic-spc", "spc"); - addMIMEType((short) 480, "chemical/x-gamess-input", "inp gam gamin"); - addMIMEType((short) 481, "chemical/x-gaussian-checkpoint", "fch fchk"); - addMIMEType((short) 482, "chemical/x-gaussian-cube", "cub"); - addMIMEType((short) 483, "chemical/x-gaussian-input", "gau gjc gjf"); - addMIMEType((short) 484, "chemical/x-gaussian-log", "gal"); - addMIMEType((short) 485, "chemical/x-gcg8-sequence", "gcg"); - addMIMEType((short) 486, "chemical/x-genbank", "gen"); - addMIMEType((short) 487, "chemical/x-hin", "hin"); - addMIMEType((short) 488, "chemical/x-isostar", "istr ist"); - addMIMEType((short) 489, "chemical/x-jcamp-dx", "jdx dx"); - addMIMEType((short) 490, "chemical/x-kinemage", "kin"); - addMIMEType((short) 491, "chemical/x-macmolecule", "mcm"); - addMIMEType((short) 492, "chemical/x-macromodel-input", "mmd mmod"); - addMIMEType((short) 493, "chemical/x-mdl-molfile", "mol"); - addMIMEType((short) 494, "chemical/x-mdl-rdfile", "rd"); - addMIMEType((short) 495, "chemical/x-mdl-rxnfile", "rxn"); - addMIMEType((short) 496, "chemical/x-mdl-sdfile", "sd sdf"); - addMIMEType((short) 497, "chemical/x-mdl-tgf", "tgf"); - addMIMEType((short) 498, "chemical/x-mmcif", "mcif"); - addMIMEType((short) 499, "chemical/x-mol2", "mol2"); - addMIMEType((short) 500, "chemical/x-molconn-Z", "b"); - addMIMEType((short) 501, "chemical/x-mopac-graph", "gpt"); - addMIMEType((short) 502, "chemical/x-mopac-input", "mop mopcrt mpc dat zmt"); - addMIMEType((short) 503, "chemical/x-mopac-out", "moo"); - addMIMEType((short) 504, "chemical/x-mopac-vib", "mvb"); - addMIMEType((short) 505, "chemical/x-ncbi-asn1", "asn"); - addMIMEType((short) 506, "chemical/x-ncbi-asn1-ascii", "prt ent"); - addMIMEType((short) 507, "chemical/x-ncbi-asn1-binary", "val aso"); - addMIMEType((short) 508, "chemical/x-ncbi-asn1-spec", "asn"); - addMIMEType((short) 509, "chemical/x-pdb", "pdb ent"); - addMIMEType((short) 510, "chemical/x-rosdal", "ros"); - addMIMEType((short) 511, "chemical/x-swissprot", "sw"); - addMIMEType((short) 512, "chemical/x-vamas-iso14976", "vms"); - addMIMEType((short) 513, "chemical/x-vmd", "vmd"); - addMIMEType((short) 514, "chemical/x-xtel", "xtel"); - addMIMEType((short) 515, "chemical/x-xyz", "xyz"); - addMIMEType((short) 516, "image/cgm"); - addMIMEType((short) 517, "image/g3fax"); - addMIMEType((short) 518, "image/gif", "gif"); - addMIMEType((short) 519, "image/ief", "ief"); - addMIMEType((short) 520, "image/jpeg", "jpeg jpg jpe"); - addMIMEType((short) 521, "image/naplps"); - addMIMEType((short) 522, "image/pcx", "pcx"); - addMIMEType((short) 523, "image/png", "png"); - addMIMEType((short) 524, "image/prs.btif"); - addMIMEType((short) 525, "image/prs.pti"); - addMIMEType((short) 526, "image/svg+xml", "svg svgz"); - addMIMEType((short) 527, "image/tiff", "tiff tif"); - addMIMEType((short) 528, "image/vnd.cns.inf2"); - addMIMEType((short) 529, "image/vnd.djvu", "djvu djv"); - addMIMEType((short) 530, "image/vnd.dwg"); - addMIMEType((short) 531, "image/vnd.dxf"); - addMIMEType((short) 532, "image/vnd.fastbidsheet"); - addMIMEType((short) 533, "image/vnd.fpx"); - addMIMEType((short) 534, "image/vnd.fst"); - addMIMEType((short) 535, "image/vnd.fujixerox.edmics-mmr"); - addMIMEType((short) 536, "image/vnd.fujixerox.edmics-rlc"); - addMIMEType((short) 537, "image/vnd.mix"); - addMIMEType((short) 538, "image/vnd.net-fpx"); - addMIMEType((short) 539, "image/vnd.svf"); - addMIMEType((short) 540, "image/vnd.wap.wbmp", "wbmp"); - addMIMEType((short) 541, "image/vnd.xiff"); - addMIMEType((short) 542, "image/x-cmu-raster", "ras"); - addMIMEType((short) 543, "image/x-coreldraw", "cdr"); - addMIMEType((short) 544, "image/x-coreldrawpattern", "pat"); - addMIMEType((short) 545, "image/x-coreldrawtemplate", "cdt"); - addMIMEType((short) 546, "image/x-corelphotopaint", "cpt"); - addMIMEType((short) 547, "image/x-icon", "ico"); - addMIMEType((short) 548, "image/x-jg", "art"); - addMIMEType((short) 549, "image/x-jng", "jng"); - addMIMEType((short) 550, "image/x-ms-bmp", "bmp"); - addMIMEType((short) 551, "image/x-photoshop", "psd"); - addMIMEType((short) 552, "image/x-portable-anymap", "pnm"); - addMIMEType((short) 553, "image/x-portable-bitmap", "pbm"); - addMIMEType((short) 554, "image/x-portable-graymap", "pgm"); - addMIMEType((short) 555, "image/x-portable-pixmap", "ppm"); - addMIMEType((short) 556, "image/x-rgb", "rgb"); - addMIMEType((short) 557, "image/x-xbitmap", "xbm"); - addMIMEType((short) 558, "image/x-xpixmap", "xpm"); - addMIMEType((short) 559, "image/x-xwindowdump", "xwd"); - addMIMEType((short) 560, "inode/chardevice"); - addMIMEType((short) 561, "inode/blockdevice"); - addMIMEType((short) 562, "inode/directory-locked"); - addMIMEType((short) 563, "inode/directory"); - addMIMEType((short) 564, "inode/fifo"); - addMIMEType((short) 565, "inode/socket"); - addMIMEType((short) 566, "message/delivery-status"); - addMIMEType((short) 567, "message/disposition-notification"); - addMIMEType((short) 568, "message/external-body"); - addMIMEType((short) 569, "message/http"); - addMIMEType((short) 570, "message/s-http"); - addMIMEType((short) 571, "message/news"); - addMIMEType((short) 572, "message/partial"); - addMIMEType((short) 573, "message/rfc822"); - addMIMEType((short) 574, "model/iges", "igs iges"); - addMIMEType((short) 575, "model/mesh", "msh mesh silo"); - addMIMEType((short) 576, "model/vnd.dwf"); - addMIMEType((short) 577, "model/vnd.flatland.3dml"); - addMIMEType((short) 578, "model/vnd.gdl"); - addMIMEType((short) 579, "model/vnd.gs-gdl"); - addMIMEType((short) 580, "model/vnd.gtw"); - addMIMEType((short) 581, "model/vnd.mts"); - addMIMEType((short) 582, "model/vnd.vtu"); - addMIMEType((short) 583, "model/vrml", "wrl vrml"); - addMIMEType((short) 584, "multipart/alternative"); - addMIMEType((short) 585, "multipart/appledouble"); - addMIMEType((short) 586, "multipart/byteranges"); - addMIMEType((short) 587, "multipart/digest"); - addMIMEType((short) 588, "multipart/encrypted"); - addMIMEType((short) 589, "multipart/form-data"); - addMIMEType((short) 590, "multipart/header-set"); - addMIMEType((short) 591, "multipart/mixed"); - addMIMEType((short) 592, "multipart/parallel"); - addMIMEType((short) 593, "multipart/related"); - addMIMEType((short) 594, "multipart/report"); - addMIMEType((short) 595, "multipart/signed"); - addMIMEType((short) 596, "multipart/voice-message"); - addMIMEType((short) 597, "text/calendar", "ics icz"); - addMIMEType((short) 598, "text/comma-separated-values", "csv"); - addMIMEType((short) 599, "text/css", "css"); - addMIMEType((short) 600, "text/directory"); - addMIMEType((short) 601, "text/english"); - addMIMEType((short) 602, "text/enriched"); - addMIMEType((short) 603, "text/h323", "323"); - addMIMEType((short) 604, "text/html", "html htm shtml"); - addMIMEType((short) 605, "text/iuls", "uls"); - addMIMEType((short) 606, "text/mathml", "mml"); - addMIMEType((short) 607, "text/parityfec"); - addMIMEType((short) 608, "text/plain", "asc txt text diff pot"); - addMIMEType((short) 609, "text/prs.lines.tag"); - addMIMEType((short) 610, "text/x-psp", "psp"); - addMIMEType((short) 611, "text/rfc822-headers"); - addMIMEType((short) 612, "text/richtext", "rtx"); - addMIMEType((short) 613, "text/rtf", "rtf"); - addMIMEType((short) 614, "text/scriptlet", "sct wsc"); - addMIMEType((short) 615, "text/t140"); - addMIMEType((short) 616, "text/texmacs", "tm ts"); - addMIMEType((short) 617, "text/tab-separated-values", "tsv"); - addMIMEType((short) 618, "text/uri-list"); - addMIMEType((short) 619, "text/vnd.abc"); - addMIMEType((short) 620, "text/vnd.curl"); - addMIMEType((short) 621, "text/vnd.DMClientScript"); - addMIMEType((short) 622, "text/vnd.flatland.3dml"); - addMIMEType((short) 623, "text/vnd.fly"); - addMIMEType((short) 624, "text/vnd.fmi.flexstor"); - addMIMEType((short) 625, "text/vnd.in3d.3dml"); - addMIMEType((short) 626, "text/vnd.in3d.spot"); - addMIMEType((short) 627, "text/vnd.IPTC.NewsML"); - addMIMEType((short) 628, "text/vnd.IPTC.NITF"); - addMIMEType((short) 629, "text/vnd.latex-z"); - addMIMEType((short) 630, "text/vnd.motorola.reflex"); - addMIMEType((short) 631, "text/vnd.ms-mediapackage"); - addMIMEType((short) 632, "text/vnd.sun.j2me.app-descriptor", "jad"); - addMIMEType((short) 633, "text/vnd.wap.si"); - addMIMEType((short) 634, "text/vnd.wap.sl"); - addMIMEType((short) 635, "text/vnd.wap.wml", "wml"); - addMIMEType((short) 636, "text/vnd.wap.wmlscript", "wmls"); - addMIMEType((short) 637, "text/x-bibtex", "bib"); - addMIMEType((short) 638, "text/x-c++hdr", "h++ hpp hxx hh"); - addMIMEType((short) 639, "text/x-c++src", "c++ cpp cxx cc"); - addMIMEType((short) 640, "text/x-chdr", "h"); - addMIMEType((short) 641, "text/x-crontab"); - addMIMEType((short) 642, "text/x-csh", "csh"); - addMIMEType((short) 643, "text/x-csrc", "c"); - addMIMEType((short) 644, "text/x-haskell", "hs"); - addMIMEType((short) 645, "text/x-java", "java"); - addMIMEType((short) 646, "text/x-literate-haskell", "lhs"); - addMIMEType((short) 647, "text/x-makefile"); - addMIMEType((short) 648, "text/x-moc", "moc"); - addMIMEType((short) 649, "text/x-pascal", "p pas"); - addMIMEType((short) 650, "text/x-pcs-gcd", "gcd"); - addMIMEType((short) 651, "text/x-perl", "pl pm"); - addMIMEType((short) 652, "text/x-python", "py"); - addMIMEType((short) 653, "text/x-server-parsed-html"); - addMIMEType((short) 654, "text/x-setext", "etx"); - addMIMEType((short) 655, "text/x-sh", "sh"); - addMIMEType((short) 656, "text/x-tcl", "tcl tk"); - addMIMEType((short) 657, "text/x-tex", "tex ltx sty cls"); - addMIMEType((short) 658, "text/x-vcalendar", "vcs"); - addMIMEType((short) 659, "text/x-vcard", "vcf"); - addMIMEType((short) 660, "video/dl", "dl"); - addMIMEType((short) 661, "video/dv", "dif dv"); - addMIMEType((short) 662, "video/fli", "fli"); - addMIMEType((short) 663, "video/gl", "gl"); - addMIMEType((short) 664, "video/mpeg", "mpeg mpg mpe"); - addMIMEType((short) 665, "video/mp4", "mp4"); - addMIMEType((short) 666, "video/quicktime", "qt mov"); - addMIMEType((short) 667, "video/mp4v-es"); - addMIMEType((short) 668, "video/parityfec"); - addMIMEType((short) 669, "video/pointer"); - addMIMEType((short) 670, "video/vnd.fvt"); - addMIMEType((short) 671, "video/vnd.motorola.video"); - addMIMEType((short) 672, "video/vnd.motorola.videop"); - addMIMEType((short) 673, "video/vnd.mpegurl", "mxu"); - addMIMEType((short) 674, "video/vnd.mts"); - addMIMEType((short) 675, "video/vnd.nokia.interleaved-multimedia"); - addMIMEType((short) 676, "video/vnd.vivo"); - addMIMEType((short) 677, "video/x-la-asf", "lsf lsx"); - addMIMEType((short) 678, "video/x-mng", "mng"); - addMIMEType((short) 679, "video/x-ms-asf", "asf asx"); - addMIMEType((short) 680, "video/x-ms-wm", "wm"); - addMIMEType((short) 681, "video/x-ms-wmv", "wmv"); - addMIMEType((short) 682, "video/x-ms-wmx", "wmx"); - addMIMEType((short) 683, "video/x-ms-wvx", "wvx"); - addMIMEType((short) 684, "video/x-msvideo", "avi"); - addMIMEType((short) 685, "video/x-sgi-movie", "movie"); - addMIMEType((short) 686, "x-conference/x-cooltalk", "ice"); - addMIMEType((short) 687, "x-world/x-vrml", "vrm vrml wrl"); - } - - /** Guess a MIME type from a filename */ - public static String guessMIMEType(String arg) { - int x = arg.lastIndexOf('.'); - if(x == -1 || x == arg.length()-1) - return DEFAULT_MIME_TYPE; - String ext = arg.substring(x+1).toLowerCase(); - Short mimeIndexOb = mimeTypesByExtension.get(ext); - if(mimeIndexOb != null) { - return mimeTypesByNumber.get(mimeIndexOb.intValue()); - } - return DEFAULT_MIME_TYPE; - } - - public static String getExtension(String type) { - short typeNumber = byName(type); - if(typeNumber < 0) return null; - return primaryExtensionByMimeNumber.get(typeNumber); - } - - public static String[] getAllMIMETypes() { - return mimeTypesByNumber.toArray(new String[mimeTypesByNumber.size()]); - } - -} diff --git a/src/de/todesbaum/util/swing/SortedListModel.java b/src/de/todesbaum/util/swing/SortedListModel.java deleted file mode 100644 index 39a5ce2..0000000 --- a/src/de/todesbaum/util/swing/SortedListModel.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * jSite - SortedListModel.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.swing; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; - -import javax.swing.AbstractListModel; - -/** - * @param - * The type of the elements - * @author David Roden <droden@gmail.com> - */ -public class SortedListModel> extends AbstractListModel implements List { - - /** The elements. */ - private List elements = new ArrayList(); - - /** - * {@inheritDoc} - */ - public int getSize() { - return size(); - } - - /** - * {@inheritDoc} - */ - public Object getElementAt(int index) { - return elements.get(index); - } - - /** - * {@inheritDoc} - */ - public void add(int index, T element) { - elements.add(index, element); - Collections.sort(elements); - fireContentsChanged(this, 0, size()); - } - - /** - * {@inheritDoc} - */ - public boolean add(T o) { - boolean result = elements.add(o); - Collections.sort(elements); - fireContentsChanged(this, 0, size()); - return result; - } - - /** - * {@inheritDoc} - */ - public boolean addAll(Collection c) { - boolean result = elements.addAll(c); - Collections.sort(elements); - fireContentsChanged(this, 0, size()); - return result; - } - - /** - * {@inheritDoc} - */ - public boolean addAll(int index, Collection c) { - boolean result = elements.addAll(index, c); - Collections.sort(elements); - fireContentsChanged(this, 0, size()); - return result; - } - - /** - * {@inheritDoc} - */ - public void clear() { - elements.clear(); - fireContentsChanged(this, 0, size()); - } - - /** - * {@inheritDoc} - */ - public boolean contains(Object o) { - return elements.contains(o); - } - - /** - * {@inheritDoc} - */ - public boolean containsAll(Collection c) { - return elements.containsAll(c); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean equals(Object o) { - return elements.equals(o); - } - - /** - * {@inheritDoc} - */ - public T get(int index) { - return elements.get(index); - } - - /** - * {@inheritDoc} - */ - @Override - public int hashCode() { - return elements.hashCode(); - } - - /** - * {@inheritDoc} - */ - public int indexOf(Object o) { - return elements.indexOf(o); - } - - /** - * {@inheritDoc} - */ - public boolean isEmpty() { - return elements.isEmpty(); - } - - /** - * {@inheritDoc} - */ - public Iterator iterator() { - return elements.iterator(); - } - - /** - * {@inheritDoc} - */ - public int lastIndexOf(Object o) { - return elements.lastIndexOf(o); - } - - /** - * {@inheritDoc} - */ - public ListIterator listIterator() { - return elements.listIterator(); - } - - /** - * {@inheritDoc} - */ - public ListIterator listIterator(int index) { - return elements.listIterator(index); - } - - /** - * {@inheritDoc} - */ - public T remove(int index) { - fireContentsChanged(this, 0, size()); - return elements.remove(index); - } - - /** - * {@inheritDoc} - */ - public boolean remove(Object o) { - fireContentsChanged(this, 0, size()); - return elements.remove(o); - } - - /** - * {@inheritDoc} - */ - public boolean removeAll(Collection c) { - fireContentsChanged(this, 0, size()); - return elements.removeAll(c); - } - - /** - * {@inheritDoc} - */ - public boolean retainAll(Collection c) { - fireContentsChanged(this, 0, size()); - return elements.retainAll(c); - } - - /** - * {@inheritDoc} - */ - public T set(int index, T element) { - T result = elements.set(index, element); - Collections.sort(elements); - fireContentsChanged(this, 0, size()); - return result; - } - - /** - * {@inheritDoc} - */ - public int size() { - return elements.size(); - } - - /** - * {@inheritDoc} - */ - public List subList(int fromIndex, int toIndex) { - return elements.subList(fromIndex, toIndex); - } - - /** - * {@inheritDoc} - */ - public Object[] toArray() { - return elements.toArray(); - } - - /** - * {@inheritDoc} - */ - public U[] toArray(U[] a) { - return elements.toArray(a); - } - -} diff --git a/src/de/todesbaum/util/swing/TLabel.java b/src/de/todesbaum/util/swing/TLabel.java deleted file mode 100644 index 0393f5c..0000000 --- a/src/de/todesbaum/util/swing/TLabel.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * jSite - TLabel.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.swing; - -import java.awt.Component; - -import javax.swing.Icon; -import javax.swing.JLabel; - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class TLabel extends JLabel { - - public TLabel() { - super(); - } - - public TLabel(int mnemonic, Component labelFor) { - super(); - setDisplayedMnemonic(mnemonic); - setLabelFor(labelFor); - } - - public TLabel(Icon image) { - super(image); - } - - public TLabel(Icon image, int mnemonic, Component labelFor) { - super(image); - setDisplayedMnemonic(mnemonic); - setLabelFor(labelFor); - } - - public TLabel(Icon image, int horizontalAlignment) { - super(image); - } - - public TLabel(Icon image, int horizontalAlignment, int mnemonic, Component labelFor) { - super(image); - setDisplayedMnemonic(mnemonic); - setLabelFor(labelFor); - } - - public TLabel(String text) { - super(text); - } - - public TLabel(String text, int mnemonic, Component labelFor) { - super(text); - setDisplayedMnemonic(mnemonic); - setLabelFor(labelFor); - setAlignmentX(0.0f); - } - - public TLabel(String text, Icon icon, int horizontalAlignment) { - super(text, icon, horizontalAlignment); - } - - public TLabel(String text, Icon icon, int horizontalAlignment, int mnemonic, Component labelFor) { - super(text, icon, horizontalAlignment); - setDisplayedMnemonic(mnemonic); - setLabelFor(labelFor); - } - - public TLabel(String text, int horizontalAlignment) { - super(text, horizontalAlignment); - } - - public TLabel(String text, int horizontalAlignment, int mnemonic, Component labelFor) { - super(text, horizontalAlignment); - setDisplayedMnemonic(mnemonic); - setLabelFor(labelFor); - } - -} diff --git a/src/de/todesbaum/util/swing/TWizard.java b/src/de/todesbaum/util/swing/TWizard.java deleted file mode 100644 index 464ac69..0000000 --- a/src/de/todesbaum/util/swing/TWizard.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * jSite - TWizard.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.swing; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Font; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.WindowEvent; -import java.awt.event.WindowListener; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.Icon; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.SwingConstants; -import javax.swing.border.EmptyBorder; - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class TWizard extends JFrame implements WindowListener { - - protected List wizardListeners = new ArrayList(); - - private Action previousAction; - private Action nextAction; - private Action quitAction; - private JLabel pageIcon; - private JPanel pagePanel; - private JLabel pageHeading; - private JLabel pageDescription; - - @Override - protected void frameInit() { - super.frameInit(); - setResizable(false); - addWindowListener(this); - setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); - createActions(); - - pageIcon = new JLabel(); - pageIcon.setVerticalAlignment(SwingConstants.TOP); - pageHeading = new JLabel(); - pageHeading.setFont(pageHeading.getFont().deriveFont(pageHeading.getFont().getSize() * 2.0f).deriveFont(Font.BOLD)); - pageDescription = new JLabel(); - - JPanel contentPane = new JPanel(new BorderLayout(12, 12)); - contentPane.setBorder(new EmptyBorder(12, 12, 12, 12)); - - JPanel topPanel = new JPanel(new BorderLayout(12, 12)); - contentPane.add(topPanel, BorderLayout.PAGE_START); - - topPanel.add(pageIcon, BorderLayout.LINE_START); - - JPanel textPanel = new JPanel(new BorderLayout(12, 12)); - topPanel.add(textPanel, BorderLayout.CENTER); - textPanel.add(pageHeading, BorderLayout.PAGE_START); - textPanel.add(pageDescription, BorderLayout.CENTER); - - pagePanel = new JPanel(new BorderLayout(12, 12)); - contentPane.add(pagePanel, BorderLayout.CENTER); - - JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12)); - buttonPanel.setBorder(new EmptyBorder(-12, -12, -12, -12)); - buttonPanel.add(new JButton(previousAction)); - buttonPanel.add(new JButton(nextAction)); - buttonPanel.add(new JButton(quitAction)); - contentPane.add(buttonPanel, BorderLayout.PAGE_END); - - setContentPane(contentPane); - } - - @Override - public void pack() { - super.pack(); - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - setLocation((screenSize.width - getWidth()) / 2, (screenSize.height - getHeight()) / 2); - // System.out.println("resized to: " + getWidth() + "x" + getHeight()); - } - - private void createActions() { - previousAction = new AbstractAction("Previous") { - public void actionPerformed(ActionEvent actionEvent) { - actionPrevious(); - } - }; - - nextAction = new AbstractAction("Next") { - public void actionPerformed(ActionEvent actionEvent) { - actionNext(); - } - }; - - quitAction = new AbstractAction("Quit") { - public void actionPerformed(ActionEvent actionEvent) { - actionQuit(); - } - }; - } - - public void addWizardListener(WizardListener wizardListener) { - wizardListeners.add(wizardListener); - } - - public void removeWizardListener(WizardListener wizardListener) { - wizardListeners.remove(wizardListener); - } - - protected void fireWizardPreviousPressed() { - for (WizardListener wizardListener: wizardListeners) { - wizardListener.wizardPreviousPressed(this); - } - } - - protected void fireWizardNextPressed() { - for (WizardListener wizardListener: wizardListeners) { - wizardListener.wizardNextPressed(this); - } - } - - protected void fireWizardQuitPressed() { - for (WizardListener wizardListener: wizardListeners) { - wizardListener.wizardQuitPressed(this); - } - } - - public void setIcon(Icon icon) { - pageIcon.setIcon(icon); - } - - public void setPage(TWizardPage page) { - setVisible(false); - pageHeading.setText(page.getHeading()); - pageDescription.setText(page.getDescription()); - if (pagePanel.getComponentCount() > 0) { - if (pagePanel.getComponent(0) instanceof TWizardPage) { - ((TWizardPage) pagePanel.getComponent(0)).pageDeleted(this); - } - } - pagePanel.removeAll(); - pagePanel.add(page, BorderLayout.CENTER); - page.pageAdded(this); - pack(); - setTitle(page.getHeading()); - setVisible(true); - } - - public TWizardPage getPage() { - return (TWizardPage) pagePanel.getComponent(0); - } - - public void setPreviousEnabled(boolean previousEnabled) { - previousAction.setEnabled(previousEnabled); - } - - public void setPreviousName(String previousName) { - previousAction.putValue(Action.NAME, previousName); - } - - public void setNextEnabled(boolean nextEnabled) { - nextAction.setEnabled(nextEnabled); - } - - public void setNextName(String nextName) { - nextAction.putValue(Action.NAME, nextName); - } - - public void setQuitEnabled(boolean quitEnabled) { - quitAction.setEnabled(quitEnabled); - } - - public void setQuitName(String quitName) { - quitAction.putValue(Action.NAME, quitName); - } - - protected void actionPrevious() { - fireWizardPreviousPressed(); - } - - protected void actionNext() { - fireWizardNextPressed(); - } - - protected void actionQuit() { - fireWizardQuitPressed(); - } - - // - // INTERFACE WindowListener - // - - /** - * {@inheritDoc} - */ - public void windowOpened(WindowEvent e) { - } - - /** - * {@inheritDoc} - */ - public void windowClosing(WindowEvent e) { - fireWizardQuitPressed(); - } - - /** - * {@inheritDoc} - */ - public void windowClosed(WindowEvent e) { - } - - /** - * {@inheritDoc} - */ - public void windowIconified(WindowEvent e) { - } - - /** - * {@inheritDoc} - */ - public void windowDeiconified(WindowEvent e) { - } - - /** - * {@inheritDoc} - */ - public void windowActivated(WindowEvent e) { - } - - /** - * {@inheritDoc} - */ - public void windowDeactivated(WindowEvent e) { - } - -} diff --git a/src/de/todesbaum/util/swing/TWizardPage.java b/src/de/todesbaum/util/swing/TWizardPage.java deleted file mode 100644 index a10b475..0000000 --- a/src/de/todesbaum/util/swing/TWizardPage.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * jSite - TWizardPage.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.swing; - -import javax.swing.JPanel; - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public class TWizardPage extends JPanel { - - protected final TWizard wizard; - protected String heading; - protected String description; - - public TWizardPage(final TWizard wizard) { - this.wizard = wizard; - } - - public TWizardPage(final TWizard wizard, String heading) { - this.wizard = wizard; - this.heading = heading; - } - - public TWizardPage(final TWizard wizard, String heading, String description) { - this(wizard, heading); - this.description = description; - } - - /** - * @return Returns the description. - */ - public String getDescription() { - return description; - } - - /** - * @param description - * The description to set. - */ - public void setDescription(String description) { - this.description = description; - } - - /** - * @return Returns the heading. - */ - public String getHeading() { - return heading; - } - - /** - * @param heading - * The heading to set. - */ - public void setHeading(String heading) { - this.heading = heading; - } - - public void pageAdded(TWizard wizard) { - } - - public void pageDeleted(TWizard wizard) { - } - -} diff --git a/src/de/todesbaum/util/swing/WizardListener.java b/src/de/todesbaum/util/swing/WizardListener.java deleted file mode 100644 index 987075d..0000000 --- a/src/de/todesbaum/util/swing/WizardListener.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * jSite - WizardListener.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.swing; - -import java.util.EventListener; - - -/** - * @author David Roden <droden@gmail.com> - * @version $Id$ - */ -public interface WizardListener extends EventListener { - - public void wizardNextPressed(TWizard wizard); - public void wizardPreviousPressed(TWizard wizard); - public void wizardQuitPressed(TWizard wizard); - -} diff --git a/src/de/todesbaum/util/xml/SimpleXML.java b/src/de/todesbaum/util/xml/SimpleXML.java deleted file mode 100644 index 8351581..0000000 --- a/src/de/todesbaum/util/xml/SimpleXML.java +++ /dev/null @@ -1,338 +0,0 @@ -/* - * jSite - SimpleXML.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.xml; - -import java.util.ArrayList; -import java.util.List; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.Text; - -/** - * SimpleXML is a helper class to construct XML trees in a fast and simple way. - * Construct a new XML tree by calling {@link #SimpleXML(String)} and append new - * nodes by calling {@link #append(String)}. - * - * @author David Roden <droden@gmail.com> - * @version $Id:SimpleXML.java 221 2006-03-06 14:46:49Z bombe $ - */ -public class SimpleXML { - - /** - * A {@link List} containing all child nodes of this node. - */ - private List children = new ArrayList(); - - /** - * The name of this node. - */ - private String name = null; - - /** - * The value of this node. - */ - private String value = null; - - /** - * Constructs a new XML node without a name. - */ - public SimpleXML() { - super(); - } - - /** - * Constructs a new XML node with the specified name. - * - * @param name - * The name of the new node - */ - public SimpleXML(String name) { - this.name = name; - } - - /** - * Returns the child node of this node with the specified name. If there are - * several child nodes with the specified name only the first node is - * returned. - * - * @param nodeName - * The name of the child node - * @return The child node, or null if there is no child node - * with the specified name - */ - public SimpleXML getNode(String nodeName) { - for (int index = 0, count = children.size(); index < count; index++) { - if (children.get(index).name.equals(nodeName)) { - return children.get(index); - } - } - return null; - } - - /** - * Returns the child node that is specified by the names. The first element - * of nodeNames is the name of the child node of this node, the - * second element of nodeNames is the name of a child node's - * child node, and so on. By using this method you can descend into an XML - * tree pretty fast. - * - *

-	 *
-	 * SimpleXML deepNode = topNode.getNodes(new String[] { "person", "address", "number" });
-	 * 
- * - * @param nodeNames - * @return A node that is a deep child of this node, or null if - * the specified node does not eixst - */ - public SimpleXML getNode(String[] nodeNames) { - SimpleXML node = this; - for (String nodeName : nodeNames) { - node = node.getNode(nodeName); - } - return node; - } - - /** - * Returns all child nodes of this node. - * - * @return All child nodes of this node - */ - public SimpleXML[] getNodes() { - return getNodes(null); - } - - /** - * Returns all child nodes of this node with the specified name. If there - * are no child nodes with the specified name an empty array is returned. - * - * @param nodeName - * The name of the nodes to retrieve, or null to - * retrieve all nodes - * @return All child nodes with the specified name - */ - public SimpleXML[] getNodes(String nodeName) { - List resultList = new ArrayList(); - for (SimpleXML child : children) { - if ((nodeName == null) || child.name.equals(nodeName)) { - resultList.add(child); - } - } - return resultList.toArray(new SimpleXML[resultList.size()]); - } - - /** - * Appends a new XML node with the specified name and returns the new node. - * With this method you can create deep structures very fast. - * - *
-	 *
-	 * SimpleXML mouseNode = topNode.append("computer").append("bus").append("usb").append("mouse");
-	 * 
- * - * @param nodeName - * The name of the node to append as a child to this node - * @return The new node - */ - public SimpleXML append(String nodeName) { - return append(new SimpleXML(nodeName)); - } - - /** - * Appends a new XML node with the specified name and value and returns the - * new node. - * - * @param nodeName - * The name of the node to append - * @param nodeValue - * The value of the node to append - * @return The newly appended node - */ - public SimpleXML append(String nodeName, String nodeValue) { - return append(nodeName).setValue(nodeValue); - } - - /** - * Appends the node with all its child nodes to this node and returns the - * child node. - * - * @param newChild - * The node to append as a child - * @return The child node that was appended - */ - public SimpleXML append(SimpleXML newChild) { - children.add(newChild); - return newChild; - } - - public void remove(SimpleXML child) { - children.remove(child); - } - - public void remove(String childName) { - SimpleXML child = getNode(childName); - if (child != null) { - remove(child); - } - } - - public void replace(String childName, String value) { - remove(childName); - append(childName, value); - } - - public void replace(SimpleXML childNode) { - remove(childNode.getName()); - append(childNode); - } - - public void removeAll() { - children.clear(); - } - - /** - * Sets the value of this node. - * - * @param nodeValue - * The new value of this node - * @return This node - */ - public SimpleXML setValue(String nodeValue) { - value = nodeValue; - return this; - } - - /** - * Returns the name of this node. - * - * @return The name of this node - */ - public String getName() { - return name; - } - - /** - * Returns the value of this node. - * - * @return The value of this node - */ - public String getValue() { - return value; - } - - /** - * Returns the value of this node. If the node does not have a value, the - * given default value is returned. - * - *@param defaultValue - * The default value to return if the node does not have a value - * @return The value of this node - */ - public String getValue(String defaultValue) { - return (value == null) ? defaultValue : value; - } - - /** - * Creates a {@link Document} from this node and all its child nodes. - * - * @return The {@link Document} created from this node - */ - public Document getDocument() { - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - try { - DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); - Document document = documentBuilder.newDocument(); - Element rootElement = document.createElement(name); - document.appendChild(rootElement); - addChildren(rootElement); - return document; - } catch (ParserConfigurationException e) { - } - return null; - } - - /** - * Appends all children of this node to the specified {@link Element}. If a - * node has a value that is not null the value is appended as a - * text node. - * - * @param rootElement - * The element to attach this node's children to - */ - private void addChildren(Element rootElement) { - for (SimpleXML child : children) { - Element childElement = rootElement.getOwnerDocument().createElement(child.name); - rootElement.appendChild(childElement); - if (child.value != null) { - Text childText = rootElement.getOwnerDocument().createTextNode(child.value); - childElement.appendChild(childText); - } else { - child.addChildren(childElement); - } - } - } - - /** - * Creates a SimpleXML node from the specified {@link Document}. The - * SimpleXML node of the document's top-level node is returned. - * - * @param document - * The {@link Document} to create a SimpleXML node from - * @return The SimpleXML node created from the document's top-level node - */ - public static SimpleXML fromDocument(Document document) { - SimpleXML xmlDocument = new SimpleXML(document.getFirstChild().getNodeName()); - document.normalizeDocument(); - return addDocumentChildren(xmlDocument, document.getFirstChild()); - } - - /** - * Appends the child nodes of the specified {@link Document} to this node. - * Text nodes are converted into a node's value. - * - * @param xmlDocument - * The SimpleXML node to append the child nodes to - * @param document - * The document whose child nodes to append - * @return The SimpleXML node the child nodes were appended to - */ - private static SimpleXML addDocumentChildren(SimpleXML xmlDocument, Node document) { - NodeList childNodes = document.getChildNodes(); - for (int childIndex = 0, childCount = childNodes.getLength(); childIndex < childCount; childIndex++) { - Node childNode = childNodes.item(childIndex); - if ((childNode.getChildNodes().getLength() == 1) && (childNode.getFirstChild().getNodeName().equals("#text"))) { - xmlDocument.append(childNode.getNodeName(), childNode.getFirstChild().getNodeValue()); - } else { - if (!childNode.getNodeName().equals("#text") || (childNode.getChildNodes().getLength() != 0)) { - SimpleXML newXML = xmlDocument.append(childNode.getNodeName()); - addDocumentChildren(newXML, childNode); - } - } - } - return xmlDocument; - } - -} diff --git a/src/de/todesbaum/util/xml/XML.java b/src/de/todesbaum/util/xml/XML.java deleted file mode 100644 index 8b6b259..0000000 --- a/src/de/todesbaum/util/xml/XML.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * jSite - XML.java - Copyright © 2006–2012 David Roden - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by the Free Software - * Foundation; either version 2 of the License, or (at your option) any later - * version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 59 Temple - * Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -package de.todesbaum.util.xml; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.nio.charset.Charset; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; - -import org.w3c.dom.Document; - - -/** - * Contains method to transform DOM XML trees to byte arrays and vice versa. - * - * @author David Roden <droden@gmail.com> - * @version $Id:XML.java 221 2006-03-06 14:46:49Z bombe $ - */ -public class XML { - - /** Cached document builder factory. */ - private static DocumentBuilderFactory documentBuilderFactory = null; - - /** Cached document builder. */ - private static DocumentBuilder documentBuilder = null; - - /** Cached transformer factory. */ - private static TransformerFactory transformerFactory = null; - - /** Does nothing. */ - private XML() { - } - - /** - * Returns a document builder factory. If possible the cached instance will be returned. - * - * @return A document builder factory - */ - private static DocumentBuilderFactory getDocumentBuilderFactory() { - if (documentBuilderFactory != null) { - return documentBuilderFactory; - } - documentBuilderFactory = DocumentBuilderFactory.newInstance(); - return documentBuilderFactory; - } - - /** - * Returns a document builder. If possible the cached instance will be returned. - * - * @return A document builder - */ - private static DocumentBuilder getDocumentBuilder() { - if (documentBuilder != null) { - return documentBuilder; - } - try { - documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); - } catch (ParserConfigurationException e) { - } - return documentBuilder; - } - - /** - * Returns a transformer factory. If possible the cached instance will be returned. - * - * @return A transformer factory - */ - private static TransformerFactory getTransformerFactory() { - if (transformerFactory != null) { - return transformerFactory; - } - transformerFactory = TransformerFactory.newInstance(); - return transformerFactory; - } - - /** - * Creates a new XML document. - * - * @return A new XML document - */ - public static Document createDocument() { - return getDocumentBuilder().newDocument(); - } - - /** - * Transforms the DOM XML document into a byte array. - * - * @param document - * The document to transform - * @return The byte array containing the XML representation - */ - public static byte[] transformToByteArray(Document document) { - ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); - OutputStreamWriter converter = new OutputStreamWriter(byteOutput, Charset.forName("UTF-8")); - Result transformResult = new StreamResult(converter); - Source documentSource = new DOMSource(document); - try { - Transformer transformer = getTransformerFactory().newTransformer(); - transformer.transform(documentSource, transformResult); - byteOutput.close(); - return byteOutput.toByteArray(); - } catch (IOException ioe1) { - } catch (TransformerConfigurationException tce1) { - } catch (TransformerException te1) { - } finally { - try { - byteOutput.close(); - } catch (IOException ioe1) { - } - } - return null; - } - - /** - * Transforms the byte array into a DOM XML document. - * - * @param data - * The byte array to parse - * @return The DOM XML document - */ - public static Document transformToDocument(byte[] data) { - ByteArrayInputStream byteInput = new ByteArrayInputStream(data); - InputStreamReader converter = new InputStreamReader(byteInput, Charset.forName("UTF-8")); - Source xmlSource = new StreamSource(converter); - Result xmlResult = new DOMResult(); - try { - Transformer transformer = getTransformerFactory().newTransformer(); - transformer.transform(xmlSource, xmlResult); - return (Document) ((DOMResult) xmlResult).getNode(); - } catch (TransformerConfigurationException tce1) { - } catch (TransformerException te1) { - } finally { - if (byteInput != null) - try { - byteInput.close(); - } catch (IOException ioe1) { - } - if (converter != null) - try { - converter.close(); - } catch (IOException ioe1) { - } - } - return null; - } - -} diff --git a/src/main/java/de/todesbaum/jsite/application/AbortedException.java b/src/main/java/de/todesbaum/jsite/application/AbortedException.java new file mode 100644 index 0000000..0dfd8c7 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/AbortedException.java @@ -0,0 +1,30 @@ +/* + * jSite - AbortedException.java - Copyright © 2010–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +/** + * Marker exception that signals that the user aborted an insert. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class AbortedException extends Exception { + + /* nothing here. */ + +} diff --git a/src/main/java/de/todesbaum/jsite/application/FileOption.java b/src/main/java/de/todesbaum/jsite/application/FileOption.java new file mode 100644 index 0000000..c8824de --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/FileOption.java @@ -0,0 +1,364 @@ +/* + * jSite - FileOption.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +/** + * Container for various file options. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class FileOption { + + /** The default for the insert state. */ + private static final boolean DEFAULT_INSERT = true; + + /** The default for the insert redirect state. */ + private static final boolean DEFAULT_INSERT_REDIRECT = true; + + /** The default for the custom key. */ + private static final String DEFAULT_CUSTOM_KEY = "CHK@"; + + /** The default changed name. */ + private static final String DEFAULT_CHANGED_NAME = null; + + /** The insert state. */ + private boolean insert; + + /** Whether to force an insert. */ + private boolean forceInsert; + + /** Whether to insert a redirect. */ + private boolean insertRedirect; + + /** The hash of the last insert. */ + private String lastInsertHash; + + /** The edition of the last insert. */ + private int lastInsertEdition; + + /** The filename of the last insert. */ + private String lastInsertFilename; + + /** The current hash of the file. */ + private String currentHash; + + /** The custom key. */ + private String customKey; + + /** The changed name. */ + private String changedName; + + /** The default MIME type. */ + private final String defaultMimeType; + + /** The current MIME type. */ + private String mimeType; + + /** + * Creates new file options. + * + * @param defaultMimeType + * The default MIME type of the file + */ + public FileOption(String defaultMimeType) { + insert = DEFAULT_INSERT; + insertRedirect = DEFAULT_INSERT_REDIRECT; + customKey = DEFAULT_CUSTOM_KEY; + changedName = DEFAULT_CHANGED_NAME; + this.defaultMimeType = defaultMimeType; + mimeType = defaultMimeType; + } + + /** + * Returns the custom key. The custom key is only used when + * {@link #isInsert()} and {@link #isInsertRedirect()} both return {@code + * true}. + * + * @return The custom key + */ + public String getCustomKey() { + return customKey; + } + + /** + * Sets the custom key. The custom key is only used when {@link #isInsert()} + * and {@link #isInsertRedirect()} both return {@code true}. + * + * @param customKey + * The custom key + */ + public void setCustomKey(String customKey) { + if (customKey == null) { + this.customKey = ""; + } else { + this.customKey = customKey; + } + } + + /** + * Returns whether the file should be inserted. If a file is not inserted + * and {@link #isInsertRedirect()} is also {@code false}, the file will not + * be inserted at all. + * + * @see #setCustomKey(String) + * @return true if the file should be inserted, + * false otherwise + */ + public boolean isInsert() { + return insert; + } + + /** + * Sets whether the file should be inserted. If a file is not inserted and + * {@link #isInsertRedirect()} is also {@code false}, the file will not be + * inserted at all. + * + * @param insert + * true if the file should be inserted, + * false otherwise + */ + public void setInsert(boolean insert) { + this.insert = insert; + } + + /** + * Returns whether the insert of this file should be forced, even if its + * current hash matches the last insert hash. + * + * @return {@code true} to force the insert of this file, {@code false} + * otherwise + */ + public boolean isForceInsert() { + return forceInsert; + } + + /** + * Sets whether to force the insert of this file, even if its current hash + * matches the last insert hash. + * + * @param forceInsert + * {@code true} to force the insert of this file, {@code false} + * otherwise + * @return These file options + */ + public FileOption setForceInsert(boolean forceInsert) { + this.forceInsert = forceInsert; + return this; + } + + /** + * Returns whether a redirect to a different key should be inserted. This + * will only matter if {@link #isInsert()} returns {@code false}. The key + * that should be redirected to still needs to be specified via + * {@link #setCustomKey(String)}. + * + * @return {@code true} if a redirect should be inserted, {@code false} + * otherwise + */ + public boolean isInsertRedirect() { + return insertRedirect; + } + + /** + * Sets whether a redirect should be inserted. This will only matter if + * {@link #isInsert()} returns {@code false}, i.e. it has been + * {@link #setInsert(boolean)} to {@code false}. The key that should be + * redirected to still needs to be specified via + * {@link #setCustomKey(String)}. + * + * @param insertRedirect + * {@code true} if a redirect should be inserted, {@code false} + * otherwise + */ + public void setInsertRedirect(boolean insertRedirect) { + this.insertRedirect = insertRedirect; + } + + /** + * Returns the hash of the file when it was last inserted + * + * @return The last hash of the file + */ + public String getLastInsertHash() { + return lastInsertHash; + } + + /** + * Sets the hash of the file when it was last inserted. + * + * @param lastInsertHash + * The last hash of the file + * @return These file options + */ + public FileOption setLastInsertHash(String lastInsertHash) { + this.lastInsertHash = lastInsertHash; + return this; + } + + /** + * Returns the last edition at which this file was inserted. + * + * @return The last insert edition of this file + */ + public int getLastInsertEdition() { + return lastInsertEdition; + } + + /** + * Sets the last insert edition of this file. + * + * @param lastInsertEdition + * The last insert edition of this file + * @return These file options + */ + public FileOption setLastInsertEdition(int lastInsertEdition) { + this.lastInsertEdition = lastInsertEdition; + return this; + } + + /** + * Returns the name of the file when it was last inserted. + * + * @return The name of the file at the last insert + */ + public String getLastInsertFilename() { + return lastInsertFilename; + } + + /** + * Sets the name of the file when it was last inserted. + * + * @param lastInsertFilename + * The name of the file at the last insert. + * @return These file options + */ + public FileOption setLastInsertFilename(String lastInsertFilename) { + this.lastInsertFilename = lastInsertFilename; + return this; + } + + /** + * Returns the current hash of the file. This value is ony a temporary value + * that is copied to {@link #getLastInsertHash()} when a project has + * finished inserting. + * + * @see Project#onSuccessfulInsert() + * @return The current hash of the file + */ + public String getCurrentHash() { + return currentHash; + } + + /** + * Sets the current hash of the file. + * + * @param currentHash + * The current hash of the file + * @return These file options + */ + public FileOption setCurrentHash(String currentHash) { + this.currentHash = currentHash; + return this; + } + + /** + * Returns whether this file has a changed name. Use + * {@link #getChangedName()} is this method returns {@code true}. + * + * @return {@code true} if this file has a changed name, {@code false} + * otherwise + */ + public boolean hasChangedName() { + return (changedName != null) && (changedName.length() > 0); + } + + /** + * Returns the changed name for this file. This method will return {@code + * null} or an empty {@link String} if this file should not be renamed. + * + * @return The changed name, or {@code null} if this file should not be + * renamed + */ + public String getChangedName() { + return changedName; + } + + /** + * Sets the changed name for this file. Setting the changed file to {@code + * null} or an empty {@link String} will disable renaming. + * + * @param changedName + * The new changed name for this file + */ + public void setChangedName(String changedName) { + this.changedName = changedName; + } + + /** + * Sets the MIME type of the file. Setting the MIME type to + * null will set the MIME type to the default MIME type. + * + * @param mimeType + * The MIME type of the file + */ + public void setMimeType(String mimeType) { + if (mimeType == null) { + this.mimeType = defaultMimeType; + } else { + this.mimeType = mimeType; + } + } + + /** + * Returns the MIME type of the file. If no custom MIME type has been set, + * the default MIME type is returned. + * + * @return The MIME type of the file + */ + public String getMimeType() { + return mimeType; + } + + /** + * Returns whether the options for this file have been modified, i.e. are + * not at their default values. + * + * @return true if the options have been modified, + * false if they are at default values + */ + public boolean isCustom() { + if (insert != DEFAULT_INSERT) { + return true; + } + if (!customKey.equals(DEFAULT_CUSTOM_KEY)) { + return true; + } + if (((changedName != null) && !changedName.equals(DEFAULT_CHANGED_NAME)) || ((DEFAULT_CHANGED_NAME != null) && !DEFAULT_CHANGED_NAME.equals(changedName))) { + return true; + } + if (!defaultMimeType.equals(mimeType)) { + return true; + } + if (insertRedirect != DEFAULT_INSERT_REDIRECT) { + return true; + } + return false; + } + +} diff --git a/src/main/java/de/todesbaum/jsite/application/Freenet7Interface.java b/src/main/java/de/todesbaum/jsite/application/Freenet7Interface.java new file mode 100644 index 0000000..78250b2 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/Freenet7Interface.java @@ -0,0 +1,162 @@ +/* + * jSite - Freenet7Interface.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +import java.io.IOException; + +import de.todesbaum.util.freenet.fcp2.Client; +import de.todesbaum.util.freenet.fcp2.Connection; +import de.todesbaum.util.freenet.fcp2.GenerateSSK; +import de.todesbaum.util.freenet.fcp2.Message; +import de.todesbaum.util.freenet.fcp2.Node; + +/** + * Interface for freenet-related operations. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class Freenet7Interface { + + /** Random number to differentiate several jSites. */ + private static final int number = (int) (Math.random() * Integer.MAX_VALUE); + + /** Counter. */ + private static int counter = 0; + + /** The node to connect to. */ + private Node node; + + /** The connection to the node. */ + private Connection connection; + + /** + * Sets the hostname of the node. The default port for FCP2 connections ({@link Node#DEFAULT_PORT}) + * is used. + * + * @param hostname + * The hostname of the node + */ + public void setNodeAddress(String hostname) { + node = new Node(hostname); + connection = new Connection(node, "jSite-" + number + "-connection-" + counter++); + } + + /** + * Sets the hostname and the port of the node. + * + * @param hostname + * The hostname of the node + * @param port + * The port number of the node + */ + public void setNodeAddress(String hostname, int port) { + node = new Node(hostname, port); + connection = new Connection(node, "jSite-" + number + "-connection-" + counter++); + } + + /** + * Sets hostname and port from the given node. + * + * @param node + * The node to get the hostname and port from + */ + public void setNode(de.todesbaum.jsite.application.Node node) { + if (node != null) { + this.node = new Node(node.getHostname(), node.getPort()); + connection = new Connection(node, "jSite-" + number + "-connection-" + counter++); + } else { + this.node = null; + connection = null; + } + } + + /** + * Removes the current node from the interface. + */ + public void removeNode() { + node = null; + connection = null; + } + + /** + * Returns the node this interface is connecting to. + * + * @return The node + */ + public Node getNode() { + return node; + } + + /** + * Creates a new connection to the current node with the given identifier. + * + * @param identifier + * The identifier of the connection + * @return The connection to the node + */ + public Connection getConnection(String identifier) { + return new Connection(node, identifier); + } + + /** + * Checks whether the current node is connected. If the node is not + * connected, a connection will be tried. + * + * @return true if the node is connected, false + * otherwise + * @throws IOException + * if an I/O error occurs communicating with the node + */ + public boolean isNodePresent() throws IOException { + if (!connection.isConnected()) { + return connection.connect(); + } + return true; + } + + /** + * Generates an SSK key pair. + * + * @return An array of strings, the first one being the generated private + * (insert) URI and the second one being the generated public + * (request) URI + * @throws IOException + * if an I/O error occurs communicating with the node + */ + public String[] generateKeyPair() throws IOException { + if (!isNodePresent()) { + throw new IOException("Node is offline."); + } + GenerateSSK generateSSK = new GenerateSSK(); + Client client = new Client(connection, generateSSK); + Message keypairMessage = client.readMessage(); + return new String[] { keypairMessage.get("InsertURI"), keypairMessage.get("RequestURI") }; + } + + /** + * Checks whether the interface has already been configured with a node. + * + * @return true if this interface already has a node set, + * false otherwise + */ + public boolean hasNode() { + return (node != null) && (connection != null); + } + +} diff --git a/src/main/java/de/todesbaum/jsite/application/InsertListener.java b/src/main/java/de/todesbaum/jsite/application/InsertListener.java new file mode 100644 index 0000000..9b6b332 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/InsertListener.java @@ -0,0 +1,112 @@ +/* + * jSite - InsertListener.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +import java.util.EventListener; + +/** + * Interface for objects that want to be notified abount insert events. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public interface InsertListener extends EventListener { + + /** + * Enumeration for the different error situations. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ + public static enum ErrorType { + + /** The key does already exist. */ + KEY_COLLISION, + + /** The route to the key was not found. */ + ROUTE_NOT_FOUND, + + /** The data was not found. */ + DATA_NOT_FOUND, + + /** Error in the FCP communication. */ + FCP_ERROR, + + /** General error in the communication. */ + IO_ERROR + } + + /** + * Notifies a listener that an insert has started. + * + * @param project + * The project that is now being inserted + */ + public void projectInsertStarted(Project project); + + /** + * Notifies a listener that the upload of a project has finished and the + * inserting will start now. + * + * @param project + * The project that has been uploaded + */ + public void projectUploadFinished(Project project); + + /** + * Notifies a listener that a project insert has generated a URI. + * + * @param project + * The project being inserted + * @param uri + * The generated URI + */ + public void projectURIGenerated(Project project, String uri); + + /** + * Notifies a listener that an insert has made some progress. + * + * @param project + * The project being inserted + * @param succeeded + * The number of succeeded blocks + * @param failed + * The number of failed blocks + * @param fatal + * The number of fatally failed blocks + * @param total + * The total number of blocks + * @param finalized + * true if the total number of blocks has been + * finalized, false otherwise + */ + public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized); + + /** + * Notifies a listener that a project insert has finished. + * + * @param project + * The project being inserted + * @param success + * true if the insert succeeded, false + * otherwise + * @param cause + * The cause of a failure, if any (may be null) + */ + public void projectInsertFinished(Project project, boolean success, Throwable cause); + +} diff --git a/src/main/java/de/todesbaum/jsite/application/KeyDialog.java b/src/main/java/de/todesbaum/jsite/application/KeyDialog.java new file mode 100644 index 0000000..4b64f72 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/KeyDialog.java @@ -0,0 +1,318 @@ +/* + * jSite - KeyDialog.java - Copyright © 2010–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.text.MessageFormat; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JTextField; +import javax.swing.KeyStroke; +import javax.swing.SwingConstants; + +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.jsite.i18n.I18nContainer; + +/** + * A dialog that lets the user edit the private and public key for a project. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class KeyDialog extends JDialog { + + /** Interface to the freenet node. */ + private final Freenet7Interface freenetInterface; + + /** The public key. */ + private String publicKey; + + /** The private key. */ + private String privateKey; + + /** The “OK” button’s action. */ + private Action okAction; + + /** The “Cancel” button’s action. */ + private Action cancelAction; + + /** The “Regenerate” button’s action. */ + private Action generateAction; + + /** The text field for the private key. */ + private JTextField privateKeyTextField; + + /** The text field for the public key. */ + private JTextField publicKeyTextField; + + /** Whether the dialog was cancelled. */ + private boolean cancelled; + + /** + * Creates a new key dialog. + * + * @param freenetInterface + * Interface to the freenet node + * @param parent + * The parent frame + */ + public KeyDialog(Freenet7Interface freenetInterface, JFrame parent) { + super(parent, I18n.getMessage("jsite.key-dialog.title"), true); + this.freenetInterface = freenetInterface; + addWindowListener(new WindowAdapter() { + + @Override + @SuppressWarnings("synthetic-access") + public void windowClosing(WindowEvent windowEvent) { + actionCancel(); + } + }); + initDialog(); + } + + // + // ACCESSORS + // + + /** + * Returns whether the dialog was cancelled. + * + * @return {@code true} if the dialog was cancelled, {@code false} otherwise + */ + public boolean wasCancelled() { + return cancelled; + } + + /** + * Returns the public key. + * + * @return The public key + */ + public String getPublicKey() { + return publicKey; + } + + /** + * Sets the public key. + * + * @param publicKey + * The public key + */ + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + publicKeyTextField.setText(publicKey); + pack(); + } + + /** + * Returns the private key. + * + * @return The private key + */ + public String getPrivateKey() { + return privateKey; + } + + /** + * Sets the private key. + * + * @param privateKey + * The private key + */ + public void setPrivateKey(String privateKey) { + this.privateKey = privateKey; + privateKeyTextField.setText(privateKey); + pack(); + } + + // + // ACTIONS + // + + /** + * {@inheritDoc} + */ + @Override + public void pack() { + super.pack(); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenSize.width - getWidth()) / 2, (screenSize.height - getHeight()) / 2); + } + + // + // PRIVATE METHODS + // + + /** + * Creates all necessary actions. + */ + private void createActions() { + okAction = new AbstractAction(I18n.getMessage("jsite.general.ok")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionOk(); + } + }; + okAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.ok.tooltip")); + okAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ENTER); + + cancelAction = new AbstractAction(I18n.getMessage("jsite.general.cancel")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionCancel(); + } + }; + cancelAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.cancel.tooltip")); + cancelAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_ESCAPE); + + generateAction = new AbstractAction(I18n.getMessage("jsite.key-dialog.button.generate")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionGenerate(); + } + }; + generateAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.key-dialog.button.generate.tooltip")); + generateAction.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_DOWN_MASK)); + } + + /** + * Initializes the dialog and all its components. + */ + private void initDialog() { + createActions(); + JPanel dialogPanel = new JPanel(new BorderLayout(12, 12)); + dialogPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); + + JPanel contentPanel = new JPanel(new GridBagLayout()); + dialogPanel.add(contentPanel, BorderLayout.CENTER); + + final JLabel keysLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.keys")); + contentPanel.add(keysLabel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + + final JLabel privateKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.private-key")); + contentPanel.add(privateKeyLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0)); + + privateKeyTextField = new JTextField(); + contentPanel.add(privateKeyTextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0)); + + final JLabel publicKeyLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.public-key")); + contentPanel.add(publicKeyLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + + publicKeyTextField = new JTextField(); + contentPanel.add(publicKeyTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 12, 0, 0), 0, 0)); + + final JLabel actionsLabel = new JLabel(I18n.getMessage("jsite.key-dialog.label.actions")); + contentPanel.add(actionsLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0)); + + JPanel actionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12)); + actionButtonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12)); + contentPanel.add(actionButtonPanel, new GridBagConstraints(0, 4, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0)); + + actionButtonPanel.add(new JButton(generateAction)); + + JPanel separatorPanel = new JPanel(new BorderLayout(12, 12)); + dialogPanel.add(separatorPanel, BorderLayout.PAGE_END); + separatorPanel.add(new JSeparator(SwingConstants.HORIZONTAL), BorderLayout.PAGE_START); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12)); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(-12, -12, -12, -12)); + separatorPanel.add(buttonPanel, BorderLayout.CENTER); + buttonPanel.add(new JButton(okAction)); + buttonPanel.add(new JButton(cancelAction)); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + keysLabel.setText(I18n.getMessage("jsite.key-dialog.label.keys")); + privateKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.private-key")); + publicKeyLabel.setText(I18n.getMessage("jsite.key-dialog.label.public-key")); + actionsLabel.setText(I18n.getMessage("jsite.key-dialog.label.actions")); + } + }); + + getContentPane().add(dialogPanel, BorderLayout.CENTER); + pack(); + setResizable(false); + } + + // + // PRIVATE ACTIONS + // + + /** + * Quits the dialog, accepting all changes. + */ + private void actionOk() { + publicKey = publicKeyTextField.getText(); + privateKey = privateKeyTextField.getText(); + cancelled = false; + setVisible(false); + } + + /** + * Quits the dialog, discarding all changes. + */ + private void actionCancel() { + cancelled = true; + setVisible(false); + } + + /** + * Generates a new key pair. + */ + private void actionGenerate() { + if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.generate-new-key"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + return; + } + String[] keyPair = null; + try { + keyPair = freenetInterface.generateKeyPair(); + } catch (IOException ioe1) { + JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.keygen.io-error"), ioe1.getMessage()), null, JOptionPane.ERROR_MESSAGE); + return; + } + publicKeyTextField.setText(keyPair[1].substring(keyPair[1].indexOf('@') + 1, keyPair[1].lastIndexOf('/'))); + privateKeyTextField.setText(keyPair[0].substring(keyPair[0].indexOf('@') + 1, keyPair[0].lastIndexOf('/'))); + pack(); + } + +} diff --git a/src/main/java/de/todesbaum/jsite/application/Node.java b/src/main/java/de/todesbaum/jsite/application/Node.java new file mode 100644 index 0000000..df7492f --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/Node.java @@ -0,0 +1,166 @@ +/* + * jSite - Node.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +/** + * Container for node information. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class Node extends de.todesbaum.util.freenet.fcp2.Node { + + /** The name of the node. */ + protected String name; + + /** + * Creates a new node with the given hostname and the default port. + * + * @see de.todesbaum.util.freenet.fcp2.Node#DEFAULT_PORT + * @param hostname + * The hostname of the new node + */ + public Node(String hostname) { + this(hostname, DEFAULT_PORT); + } + + /** + * Creates a new node with the given hostname and port. + * + * @param hostname + * The hostname of the new node + * @param port + * The port of the new node + */ + public Node(String hostname, int port) { + this(hostname, port, ""); + } + + /** + * Creates a new node with the given hostname, port, and name. + * + * @param hostname + * The hostname of the new node + * @param port + * The port of the new node + * @param name + * The name of the node + */ + public Node(String hostname, int port, String name) { + super(hostname, port); + this.name = name; + } + + /** + * Creates a new node that gets its settings from the given node. + * + * @param node + * The node to copy + */ + public Node(Node node) { + this(node.getHostname(), node.getPort()); + } + + /** + * Creates a new node from the given node, overwriting the name. + * + * @param node + * The node to copy from + * @param name + * The new name of the node + */ + public Node(Node node, String name) { + this(node.getHostname(), node.getPort(), name); + } + + /** + * Sets the name of the node. + * + * @param name + * The name of the node + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the name of the node. + * + * @return The name of the node + */ + public String getName() { + return name; + } + + /** + * Sets the hostname of the node. + * + * @param hostname + * The hostname of the node + */ + public void setHostname(String hostname) { + this.hostname = hostname; + } + + /** + * Sets the port of the node. + * + * @param port + * The port of the node + */ + public void setPort(int port) { + this.port = port; + } + + /** + * {@inheritDoc} + *

+ * A node is considered as being equal to this node its name, hostname, and + * port equal their counterparts in this node. + */ + @Override + public boolean equals(Object o) { + if ((o == null) || !(o instanceof Node)) { + return false; + } + Node node = (Node) o; + return name.equals(node.name) && hostname.equals(node.hostname) && (port == node.port); + } + + /** + * {@inheritDoc} + *

+ * The hashcode for a node is created from its name, its hostname, and its + * port. + */ + @Override + public int hashCode() { + return name.hashCode() ^ hostname.hashCode() ^ port; + } + + /** + * {@inheritDoc} + *

+ * Creates a textual representation of this node. + */ + @Override + public String toString() { + return name + " (" + hostname + ":" + port + ")"; + } + +} diff --git a/src/main/java/de/todesbaum/jsite/application/Project.java b/src/main/java/de/todesbaum/jsite/application/Project.java new file mode 100644 index 0000000..fa0b774 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/Project.java @@ -0,0 +1,441 @@ +/* + * jSite - Project.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +import java.io.File; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import de.todesbaum.util.mime.DefaultMIMETypes; + +/** + * Container for project information. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class Project implements Comparable { + + /** The name of the project. */ + protected String name; + + /** The description of the project. */ + protected String description; + + /** The insert URI of the project. */ + protected String insertURI; + + /** The request URI of the project. */ + protected String requestURI; + + /** The index file of the project. */ + protected String indexFile; + + /** The local path of the project. */ + protected String localPath; + + /** The remote path of the URI. */ + protected String path; + + /** The time of the last insertion. */ + protected long lastInsertionTime; + + /** The edition to insert to. */ + protected int edition; + + /** Whether to ignore hidden directory. */ + private boolean ignoreHiddenFiles; + + /** Options for files. */ + protected Map fileOptions = new HashMap(); + + /** + * Empty constructor. + */ + public Project() { + /* do nothing. */ + } + + /** + * Creates a new project from an existing one. + * + * @param project + * The project to clone + */ + public Project(Project project) { + name = project.name; + description = project.description; + insertURI = project.insertURI; + requestURI = project.requestURI; + path = project.path; + edition = project.edition; + localPath = project.localPath; + indexFile = project.indexFile; + lastInsertionTime = project.lastInsertionTime; + ignoreHiddenFiles = project.ignoreHiddenFiles; + fileOptions = new HashMap(project.fileOptions); + } + + /** + * Returns the name of the project. + * + * @return The name of the project + */ + public String getName() { + return name; + } + + /** + * Sets the name of the project. + * + * @param name + * The name of the project + */ + public void setName(String name) { + this.name = name; + } + + /** + * Returns the description of the project. + * + * @return The description of the project + */ + public String getDescription() { + return description; + } + + /** + * Sets the description of the project. + * + * @param description + * The description of the project + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Returns the local path of the project. + * + * @return The local path of the project + */ + public String getLocalPath() { + return localPath; + } + + /** + * Sets the local path of the project. + * + * @param localPath + * The local path of the project + */ + public void setLocalPath(String localPath) { + this.localPath = localPath; + } + + /** + * Returns the name of the index file of the project, relative to the + * project’s local path. + * + * @return The name of the index file of the project + */ + public String getIndexFile() { + return indexFile; + } + + /** + * Sets the name of the index file of the project, relative to the project’s + * local path. + * + * @param indexFile + * The name of the index file of the project + */ + public void setIndexFile(String indexFile) { + this.indexFile = indexFile; + } + + /** + * Returns the time the project was last inserted, in milliseconds since the + * epoch. + * + * @return The time of the last insertion + */ + public long getLastInsertionTime() { + return lastInsertionTime; + } + + /** + * Sets the time the project was last inserted, in milliseconds since the + * last epoch. + * + * @param lastInserted + * The time of the last insertion + */ + public void setLastInsertionTime(long lastInserted) { + lastInsertionTime = lastInserted; + } + + /** + * Returns the remote path of the project. The remote path is the path that + * directly follows the request URI of the project. + * + * @return The remote path of the project + */ + public String getPath() { + return path; + } + + /** + * Sets the remote path of the project. The remote path is the path that + * directly follows the request URI of the project. + * + * @param path + * The remote path of the project + */ + public void setPath(String path) { + this.path = path; + } + + /** + * Returns the insert URI of the project. + * + * @return The insert URI of the project + */ + public String getInsertURI() { + return insertURI; + } + + /** + * Sets the insert URI of the project. + * + * @param insertURI + * The insert URI of the project + */ + public void setInsertURI(String insertURI) { + this.insertURI = shortenURI(insertURI); + } + + /** + * Returns the request URI of the project. + * + * @return The request URI of the project + */ + public String getRequestURI() { + return requestURI; + } + + /** + * Sets the request URI of the project. + * + * @param requestURI + * The request URI of the project + */ + public void setRequestURI(String requestURI) { + this.requestURI = shortenURI(requestURI); + } + + /** + * Returns whether hidden files are ignored, i.e. not inserted. + * + * @return {@code true} if hidden files are not inserted, {@code false} + * otherwise + */ + public boolean isIgnoreHiddenFiles() { + return ignoreHiddenFiles; + } + + /** + * Sets whether hidden files are ignored, i.e. not inserted. + * + * @param ignoreHiddenFiles + * {@code true} if hidden files are not inserted, {@code false} + * otherwise + */ + public void setIgnoreHiddenFiles(boolean ignoreHiddenFiles) { + this.ignoreHiddenFiles = ignoreHiddenFiles; + } + + /** + * {@inheritDoc} + *

+ * This method returns the name of the project. + */ + @Override + public String toString() { + return name; + } + + /** + * Shortens the given URI by removing scheme and key-type prefixes. + * + * @param uri + * The URI to shorten + * @return The shortened URI + */ + private String shortenURI(String uri) { + String shortUri = uri; + if (shortUri.startsWith("freenet:")) { + shortUri = shortUri.substring("freenet:".length()); + } + if (shortUri.startsWith("SSK@")) { + shortUri = shortUri.substring("SSK@".length()); + } + if (shortUri.startsWith("USK@")) { + shortUri = shortUri.substring("USK@".length()); + } + if (shortUri.endsWith("/")) { + shortUri = shortUri.substring(0, shortUri.length() - 1); + } + return shortUri; + } + + /** + * Shortens the name of the given file by removing the local path of the + * project and leading file separators. + * + * @param file + * The file whose name should be shortened + * @return The shortened name of the file + */ + public String shortenFilename(File file) { + String filename = file.getPath(); + if (filename.startsWith(localPath)) { + filename = filename.substring(localPath.length()); + if (filename.startsWith(File.separator)) { + filename = filename.substring(1); + } + } + return filename; + } + + /** + * Returns the options for the file with the given name. If the file does + * not yet have any options, a new set of default options is created and + * returned. + * + * @param filename + * The name of the file, relative to the project root + * @return The options for the file + */ + public FileOption getFileOption(String filename) { + FileOption fileOption = fileOptions.get(filename); + if (fileOption == null) { + fileOption = new FileOption(DefaultMIMETypes.guessMIMEType(filename)); + fileOptions.put(filename, fileOption); + } + return fileOption; + } + + /** + * Sets options for a file. + * + * @param filename + * The filename to set the options for, relative to the project + * root + * @param fileOption + * The options to set for the file, or null to + * remove the options for the file + */ + public void setFileOption(String filename, FileOption fileOption) { + if (fileOption != null) { + fileOptions.put(filename, fileOption); + } else { + fileOptions.remove(filename); + } + } + + /** + * Returns all file options. + * + * @return All file options + */ + public Map getFileOptions() { + return Collections.unmodifiableMap(fileOptions); + } + + /** + * Sets all file options. + * + * @param fileOptions + * The file options + */ + public void setFileOptions(Map fileOptions) { + this.fileOptions.clear(); + this.fileOptions.putAll(fileOptions); + } + + /** + * {@inheritDoc} + *

+ * Projects are compared by their name only. + */ + public int compareTo(Project project) { + return name.compareToIgnoreCase(project.name); + } + + /** + * Returns the edition of the project. + * + * @return The edition of the project + */ + public int getEdition() { + return edition; + } + + /** + * Sets the edition of the project. + * + * @param edition + * The edition to set + */ + public void setEdition(int edition) { + this.edition = edition; + } + + /** + * Constructs the final request URI including the edition number. + * + * @param offset + * The offset for the edition number + * @return The final request URI + */ + public String getFinalRequestURI(int offset) { + return "USK@" + requestURI + "/" + path + "/" + (edition + offset) + "/"; + } + + /** + * Performs some post-processing on the project after it was inserted + * successfully. At the moment it copies the current hashes of all file + * options to the last insert hashes, updating the hashes for the next + * insert. + */ + public void onSuccessfulInsert() { + for (Entry fileOptionEntry : fileOptions.entrySet()) { + FileOption fileOption = fileOptionEntry.getValue(); + if ((fileOption.getCurrentHash() != null) && (fileOption.getCurrentHash().length() > 0) && (!fileOption.getCurrentHash().equals(fileOption.getLastInsertHash()) || fileOption.isForceInsert())) { + fileOption.setLastInsertEdition(edition); + fileOption.setLastInsertHash(fileOption.getCurrentHash()); + fileOption.setLastInsertFilename(fileOption.hasChangedName() ? fileOption.getChangedName() : fileOptionEntry.getKey()); + } + fileOption.setForceInsert(false); + } + } + +} diff --git a/src/main/java/de/todesbaum/jsite/application/ProjectInserter.java b/src/main/java/de/todesbaum/jsite/application/ProjectInserter.java new file mode 100644 index 0000000..b5f2e16 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/ProjectInserter.java @@ -0,0 +1,694 @@ +/* + * jSite - ProjectInserter.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.logging.Logger; + +import de.todesbaum.jsite.gui.FileScanner; +import de.todesbaum.jsite.gui.FileScanner.ScannedFile; +import de.todesbaum.jsite.gui.FileScannerListener; +import de.todesbaum.util.freenet.fcp2.Client; +import de.todesbaum.util.freenet.fcp2.ClientPutComplexDir; +import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; +import de.todesbaum.util.freenet.fcp2.Connection; +import de.todesbaum.util.freenet.fcp2.DirectFileEntry; +import de.todesbaum.util.freenet.fcp2.FileEntry; +import de.todesbaum.util.freenet.fcp2.Message; +import de.todesbaum.util.freenet.fcp2.PriorityClass; +import de.todesbaum.util.freenet.fcp2.RedirectFileEntry; +import de.todesbaum.util.freenet.fcp2.Verbosity; +import de.todesbaum.util.io.StreamCopier.ProgressListener; + +/** + * Manages project inserts. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class ProjectInserter implements FileScannerListener, Runnable { + + /** The logger. */ + private static final Logger logger = Logger.getLogger(ProjectInserter.class.getName()); + + /** Random number for FCP instances. */ + private static final int random = (int) (Math.random() * Integer.MAX_VALUE); + + /** Counter for FCP connection identifier. */ + private static int counter = 0; + + /** The list of insert listeners. */ + private List insertListeners = new ArrayList(); + + /** The freenet interface. */ + protected Freenet7Interface freenetInterface; + + /** The project to insert. */ + protected Project project; + + /** The file scanner. */ + private FileScanner fileScanner; + + /** Object used for synchronization. */ + protected final Object lockObject = new Object(); + + /** The temp directory. */ + private String tempDirectory; + + /** The current connection. */ + private Connection connection; + + /** Whether the insert is cancelled. */ + private volatile boolean cancelled = false; + + /** Progress listener for payload transfers. */ + private ProgressListener progressListener; + + /** Whether to use “early encode.” */ + private boolean useEarlyEncode; + + /** The insert priority. */ + private PriorityClass priority; + + /** The manifest putter. */ + private ManifestPutter manifestPutter; + + /** + * Adds a listener to the list of registered listeners. + * + * @param insertListener + * The listener to add + */ + public void addInsertListener(InsertListener insertListener) { + insertListeners.add(insertListener); + } + + /** + * Removes a listener from the list of registered listeners. + * + * @param insertListener + * The listener to remove + */ + public void removeInsertListener(InsertListener insertListener) { + insertListeners.remove(insertListener); + } + + /** + * Notifies all listeners that the project insert has started. + * + * @see InsertListener#projectInsertStarted(Project) + */ + protected void fireProjectInsertStarted() { + for (InsertListener insertListener : insertListeners) { + insertListener.projectInsertStarted(project); + } + } + + /** + * Notifies all listeners that the insert has generated a URI. + * + * @see InsertListener#projectURIGenerated(Project, String) + * @param uri + * The generated URI + */ + protected void fireProjectURIGenerated(String uri) { + for (InsertListener insertListener : insertListeners) { + insertListener.projectURIGenerated(project, uri); + } + } + + /** + * Notifies all listeners that the insert has made some progress. + * + * @see InsertListener#projectUploadFinished(Project) + */ + protected void fireProjectUploadFinished() { + for (InsertListener insertListener : insertListeners) { + insertListener.projectUploadFinished(project); + } + } + + /** + * Notifies all listeners that the insert has made some progress. + * + * @see InsertListener#projectInsertProgress(Project, int, int, int, int, + * boolean) + * @param succeeded + * The number of succeeded blocks + * @param failed + * The number of failed blocks + * @param fatal + * The number of fatally failed blocks + * @param total + * The total number of blocks + * @param finalized + * true if the total number of blocks has already + * been finalized, false otherwise + */ + protected void fireProjectInsertProgress(int succeeded, int failed, int fatal, int total, boolean finalized) { + for (InsertListener insertListener : insertListeners) { + insertListener.projectInsertProgress(project, succeeded, failed, fatal, total, finalized); + } + } + + /** + * Notifies all listeners the project insert has finished. + * + * @see InsertListener#projectInsertFinished(Project, boolean, Throwable) + * @param success + * true if the project was inserted successfully, + * false if it failed + * @param cause + * The cause of the failure, if any + */ + protected void fireProjectInsertFinished(boolean success, Throwable cause) { + for (InsertListener insertListener : insertListeners) { + insertListener.projectInsertFinished(project, success, cause); + } + } + + /** + * Sets the project to insert. + * + * @param project + * The project to insert + */ + public void setProject(Project project) { + this.project = project; + } + + /** + * Sets the freenet interface to use. + * + * @param freenetInterface + * The freenet interface to use + */ + public void setFreenetInterface(Freenet7Interface freenetInterface) { + this.freenetInterface = freenetInterface; + } + + /** + * Sets the temp directory to use. + * + * @param tempDirectory + * The temp directory to use, or {@code null} to use the system + * default + */ + public void setTempDirectory(String tempDirectory) { + this.tempDirectory = tempDirectory; + } + + /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public void setUseEarlyEncode(boolean useEarlyEncode) { + this.useEarlyEncode = useEarlyEncode; + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + */ + public void setPriority(PriorityClass priority) { + this.priority = priority; + } + + /** + * Sets the manifest putter to use for inserts. + * + * @param manifestPutter + * The manifest putter to use + */ + public void setManifestPutter(ManifestPutter manifestPutter) { + this.manifestPutter = manifestPutter; + } + + /** + * Starts the insert. + * + * @param progressListener + * Listener to notify on progress events + */ + public void start(ProgressListener progressListener) { + cancelled = false; + this.progressListener = progressListener; + fileScanner = new FileScanner(project); + fileScanner.addFileScannerListener(this); + new Thread(fileScanner).start(); + } + + /** + * Stops the current insert. + */ + public void stop() { + cancelled = true; + synchronized (lockObject) { + if (connection != null) { + connection.disconnect(); + } + } + } + + /** + * Creates an input stream that delivers the given file, replacing edition + * tokens in the file’s content, if necessary. + * + * @param filename + * The name of the file + * @param fileOption + * The file options + * @param edition + * The current edition + * @param length + * An array containing a single long which is used to + * return the final length of the file, after all + * replacements + * @return The input stream for the file + * @throws IOException + * if an I/O error occurs + */ + private InputStream createFileInputStream(String filename, FileOption fileOption, int edition, long[] length) throws IOException { + File file = new File(project.getLocalPath(), filename); + length[0] = file.length(); + return new FileInputStream(file); + } + + /** + * Creates a file entry suitable for handing in to + * {@link ClientPutComplexDir#addFileEntry(FileEntry)}. + * + * @param file + * The name and hash of the file to insert + * @param edition + * The current edition + * @return A file entry for the given file + */ + private FileEntry createFileEntry(ScannedFile file, int edition) { + FileEntry fileEntry = null; + String filename = file.getFilename(); + FileOption fileOption = project.getFileOption(filename); + if (fileOption.isInsert()) { + fileOption.setCurrentHash(file.getHash()); + /* check if file was modified. */ + if (!fileOption.isForceInsert() && file.getHash().equals(fileOption.getLastInsertHash())) { + /* only insert a redirect. */ + logger.log(Level.FINE, String.format("Inserting redirect to edition %d for %s.", fileOption.getLastInsertEdition(), filename)); + return new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), "SSK@" + project.getRequestURI() + "/" + project.getPath() + "-" + fileOption.getLastInsertEdition() + "/" + fileOption.getLastInsertFilename()); + } + try { + long[] fileLength = new long[1]; + InputStream fileEntryInputStream = createFileInputStream(filename, fileOption, edition, fileLength); + fileEntry = new DirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileEntryInputStream, fileLength[0]); + } catch (IOException ioe1) { + /* ignore, null is returned. */ + } + } else { + if (fileOption.isInsertRedirect()) { + fileEntry = new RedirectFileEntry(fileOption.hasChangedName() ? fileOption.getChangedName() : filename, fileOption.getMimeType(), fileOption.getCustomKey()); + } + } + return fileEntry; + } + + /** + * Validates the given project. The project will be checked for any invalid + * conditions, such as invalid insert or request keys, missing path names, + * missing default file, and so on. + * + * @param project + * The project to check + * @return The encountered warnings and errors + */ + public static CheckReport validateProject(Project project) { + CheckReport checkReport = new CheckReport(); + if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) { + checkReport.addIssue("error.no-local-path", true); + } + if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) { + checkReport.addIssue("error.no-path", true); + } + if ((project.getIndexFile() == null) || (project.getIndexFile().length() == 0)) { + checkReport.addIssue("warning.empty-index", false); + } else { + File indexFile = new File(project.getLocalPath(), project.getIndexFile()); + if (!indexFile.exists()) { + checkReport.addIssue("error.index-missing", true); + } + } + String indexFile = project.getIndexFile(); + boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0); + List 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 = fileOptionEntries.isEmpty(); + for (Entry fileOptionEntry : fileOptionEntries) { + String fileName = fileOptionEntry.getKey(); + FileOption fileOption = fileOptionEntry.getValue(); + insert |= fileOption.isInsert() || fileOption.isInsertRedirect(); + if (fileName.equals(project.getIndexFile()) && !fileOption.isInsert() && !fileOption.isInsertRedirect()) { + checkReport.addIssue("error.index-not-inserted", true); + } + if (!fileOption.isInsert() && fileOption.isInsertRedirect() && ((fileOption.getCustomKey().length() == 0) || "CHK@".equals(fileOption.getCustomKey()))) { + checkReport.addIssue("error.no-custom-key", true, fileName); + } + } + if (!insert) { + checkReport.addIssue("error.no-files-to-insert", true); + } + Set fileNames = new HashSet(); + for (Entry fileOptionEntry : fileOptionEntries) { + FileOption fileOption = fileOptionEntry.getValue(); + if (!fileOption.isInsert() && !fileOption.isInsertRedirect()) { + logger.log(Level.FINEST, "Ignoring {0}.", fileOptionEntry.getKey()); + continue; + } + String fileName = fileOptionEntry.getKey(); + if (fileOption.hasChangedName()) { + fileName = fileOption.getChangedName(); + } + logger.log(Level.FINEST, "Adding “{0}” for {1}.", new Object[] { fileName, fileOptionEntry.getKey() }); + if (!fileNames.add(fileName)) { + checkReport.addIssue("error.duplicate-file", true, fileName); + } + } + long totalSize = 0; + FileScanner fileScanner = new FileScanner(project); + final CountDownLatch completionLatch = new CountDownLatch(1); + fileScanner.addFileScannerListener(new FileScannerListener() { + + @Override + public void fileScannerFinished(FileScanner fileScanner) { + completionLatch.countDown(); + } + }); + new Thread(fileScanner).start(); + while (completionLatch.getCount() > 0) { + try { + completionLatch.await(); + } catch (InterruptedException ie1) { + /* TODO: logging */ + } + } + for (ScannedFile scannedFile : fileScanner.getFiles()) { + String fileName = scannedFile.getFilename(); + FileOption fileOption = project.getFileOption(fileName); + if ((fileOption != null) && !fileOption.isInsert()) { + continue; + } + totalSize += new File(project.getLocalPath(), fileName).length(); + } + if (totalSize > 2 * 1024 * 1024) { + checkReport.addIssue("warning.site-larger-than-2-mib", false); + } + return checkReport; + } + + /** + * {@inheritDoc} + */ + public void run() { + fireProjectInsertStarted(); + List files = fileScanner.getFiles(); + + /* create connection to node */ + synchronized (lockObject) { + connection = freenetInterface.getConnection("project-insert-" + random + counter++); + } + connection.setTempDirectory(tempDirectory); + boolean connected = false; + Throwable cause = null; + try { + connected = connection.connect(); + } catch (IOException e1) { + cause = e1; + } + + if (!connected || cancelled) { + fireProjectInsertFinished(false, cancelled ? new AbortedException() : cause); + return; + } + + Client client = new Client(connection); + + /* collect files */ + int edition = project.getEdition(); + String dirURI = "USK@" + project.getInsertURI() + "/" + project.getPath() + "/" + edition + "/"; + ClientPutComplexDir putDir = new ClientPutComplexDir("dir-" + counter++, dirURI, tempDirectory); + if ((project.getIndexFile() != null) && (project.getIndexFile().length() > 0)) { + putDir.setDefaultName(project.getIndexFile()); + } + putDir.setVerbosity(Verbosity.ALL); + putDir.setMaxRetries(-1); + putDir.setEarlyEncode(useEarlyEncode); + putDir.setPriorityClass(priority); + putDir.setManifestPutter(manifestPutter); + for (ScannedFile file : files) { + FileEntry fileEntry = createFileEntry(file, edition); + if (fileEntry != null) { + try { + putDir.addFileEntry(fileEntry); + } catch (IOException ioe1) { + fireProjectInsertFinished(false, ioe1); + return; + } + } + } + + /* start request */ + try { + client.execute(putDir, progressListener); + fireProjectUploadFinished(); + } catch (IOException ioe1) { + fireProjectInsertFinished(false, ioe1); + return; + } + + /* parse progress and success messages */ + String finalURI = null; + boolean success = false; + boolean finished = false; + boolean disconnected = false; + while (!finished && !cancelled) { + Message message = client.readMessage(); + finished = (message == null) || (disconnected = client.isDisconnected()); + logger.log(Level.FINE, "Received message: " + message); + if (!finished) { + @SuppressWarnings("null") + String messageName = message.getName(); + if ("URIGenerated".equals(messageName)) { + finalURI = message.get("URI"); + fireProjectURIGenerated(finalURI); + } + if ("SimpleProgress".equals(messageName)) { + int total = Integer.parseInt(message.get("Total")); + int succeeded = Integer.parseInt(message.get("Succeeded")); + int fatal = Integer.parseInt(message.get("FatallyFailed")); + int failed = Integer.parseInt(message.get("Failed")); + boolean finalized = Boolean.parseBoolean(message.get("FinalizedTotal")); + fireProjectInsertProgress(succeeded, failed, fatal, total, finalized); + } + success |= "PutSuccessful".equals(messageName); + finished = (success && (finalURI != null)) || "PutFailed".equals(messageName) || messageName.endsWith("Error"); + } + } + + /* post-insert work */ + if (success) { + @SuppressWarnings("null") + String editionPart = finalURI.substring(finalURI.lastIndexOf('/') + 1); + int newEdition = Integer.parseInt(editionPart); + project.setEdition(newEdition); + project.setLastInsertionTime(System.currentTimeMillis()); + project.onSuccessfulInsert(); + } + fireProjectInsertFinished(success, cancelled ? new AbortedException() : (disconnected ? new IOException("Connection terminated") : null)); + } + + // + // INTERFACE FileScannerListener + // + + /** + * {@inheritDoc} + */ + public void fileScannerFinished(FileScanner fileScanner) { + if (!fileScanner.isError()) { + new Thread(this).start(); + } else { + fireProjectInsertFinished(false, null); + } + fileScanner.removeFileScannerListener(this); + } + + /** + * Container class that collects all warnings and errors that occured during + * {@link ProjectInserter#validateProject(Project) project validation}. + * + * @author David ‘Bombe’ Roden + */ + public static class CheckReport implements Iterable { + + /** The issures that occured. */ + private final List issues = new ArrayList(); + + /** + * Adds an issue. + * + * @param issue + * The issue to add + */ + public void addIssue(Issue issue) { + issues.add(issue); + } + + /** + * Creates an {@link Issue} from the given error key and fatality flag + * and {@link #addIssue(Issue) adds} it. + * + * @param errorKey + * The error key + * @param fatal + * {@code true} if the error is fatal, {@code false} if only + * a warning should be generated + * @param parameters + * Any additional parameters + */ + public void addIssue(String errorKey, boolean fatal, String... parameters) { + addIssue(new Issue(errorKey, fatal, parameters)); + } + + /** + * {@inheritDoc} + */ + public Iterator iterator() { + return issues.iterator(); + } + + /** + * Returns whether this check report does not contain any errors. + * + * @return {@code true} if this check report does not contain any + * errors, {@code false} if this check report does contain + * errors + */ + public boolean isEmpty() { + return issues.isEmpty(); + } + + /** + * Returns the number of issues in this check report. + * + * @return The number of issues + */ + public int size() { + return issues.size(); + } + + } + + /** + * Container class for a single issue. An issue contains an error key + * that describes the error, and a fatality flag that determines whether + * the insert has to be aborted (if the flag is {@code true}) or if it + * can still be performed and only a warning should be generated (if the + * flag is {@code false}). + * + * @author David ‘Bombe’ + * Roden + */ + public static class Issue { + + /** The error key. */ + private final String errorKey; + + /** The fatality flag. */ + private final boolean fatal; + + /** Additional parameters. */ + private String[] parameters; + + /** + * Creates a new issue. + * + * @param errorKey + * The error key + * @param fatal + * The fatality flag + * @param parameters + * Any additional parameters + */ + protected Issue(String errorKey, boolean fatal, String... parameters) { + this.errorKey = errorKey; + this.fatal = fatal; + this.parameters = parameters; + } + + /** + * Returns the key of the encountered error. + * + * @return The error key + */ + public String getErrorKey() { + return errorKey; + } + + /** + * Returns whether the issue is fatal and the insert has to be + * aborted. Otherwise only a warning should be shown. + * + * @return {@code true} if the insert needs to be aborted, {@code + * false} otherwise + */ + public boolean isFatal() { + return fatal; + } + + /** + * Returns any additional parameters. + * + * @return The additional parameters + */ + public String[] getParameters() { + return parameters; + } + + } + +} diff --git a/src/main/java/de/todesbaum/jsite/application/UpdateChecker.java b/src/main/java/de/todesbaum/jsite/application/UpdateChecker.java new file mode 100644 index 0000000..c49f166 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/UpdateChecker.java @@ -0,0 +1,287 @@ +/* + * jSite - UpdateChecker.java - Copyright © 2008–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import de.todesbaum.jsite.main.Main; +import de.todesbaum.jsite.main.Version; +import de.todesbaum.util.freenet.fcp2.Client; +import de.todesbaum.util.freenet.fcp2.ClientGet; +import de.todesbaum.util.freenet.fcp2.Connection; +import de.todesbaum.util.freenet.fcp2.Message; +import de.todesbaum.util.freenet.fcp2.Persistence; +import de.todesbaum.util.freenet.fcp2.ReturnType; +import de.todesbaum.util.freenet.fcp2.Verbosity; +import de.todesbaum.util.io.Closer; + +/** + * Checks for newer versions of jSite. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class UpdateChecker implements Runnable { + + /** The logger. */ + private static final Logger logger = Logger.getLogger(UpdateChecker.class.getName()); + + /** Counter for connection names. */ + private static int counter = 0; + + /** The edition for the update check URL. */ + private static final int UPDATE_EDITION = 17; + + /** The URL for update checks. */ + private static final String UPDATE_KEY = "USK@e3myoFyp5avg6WYN16ImHri6J7Nj8980Fm~aQe4EX1U,QvbWT0ImE0TwLODTl7EoJx2NBnwDxTbLTE6zkB-eGPs,AQACAAE"; + + /** Object used for synchronization. */ + private final Object syncObject = new Object(); + + /** Update listeners. */ + private final List updateListeners = new ArrayList(); + + /** Whether the main thread should stop. */ + private boolean shouldStop = false; + + /** Current last found edition of update key. */ + private int lastUpdateEdition = UPDATE_EDITION; + + /** Last found version. */ + private Version lastVersion = Main.getVersion(); + + /** The freenet interface. */ + private final Freenet7Interface freenetInterface; + + /** + * Creates a new update checker that uses the given frame as its parent and + * communications via the given freenet interface. + * + * @param freenetInterface + * The freenet interface + */ + public UpdateChecker(Freenet7Interface freenetInterface) { + this.freenetInterface = freenetInterface; + } + + // + // EVENT LISTENER MANAGEMENT + // + + /** + * Adds an update listener to the list of registered listeners. + * + * @param updateListener + * The update listener to add + */ + public void addUpdateListener(UpdateListener updateListener) { + updateListeners.add(updateListener); + } + + /** + * Removes the given listener from the list of registered listeners. + * + * @param updateListener + * The update listener to remove + */ + public void removeUpdateListener(UpdateListener updateListener) { + updateListeners.remove(updateListener); + } + + /** + * Notifies all listeners that a version was found. + * + * @param foundVersion + * The version that was found + * @param versionTimestamp + * The timestamp of the version + */ + protected void fireUpdateFound(Version foundVersion, long versionTimestamp) { + for (UpdateListener updateListener : updateListeners) { + updateListener.foundUpdateData(foundVersion, versionTimestamp); + } + } + + // + // ACCESSORS + // + + /** + * Returns the latest version that was found. + * + * @return The latest found version + */ + public Version getLatestVersion() { + return lastVersion; + } + + // + // ACTIONS + // + + /** + * Starts the update checker. + */ + public void start() { + new Thread(this).start(); + } + + /** + * Stops the update checker. + */ + public void stop() { + synchronized (syncObject) { + shouldStop = true; + syncObject.notifyAll(); + } + } + + // + // PRIVATE METHODS + // + + /** + * Returns whether the update checker should stop. + * + * @return true if the update checker should stop, + * false otherwise + */ + private boolean shouldStop() { + synchronized (syncObject) { + return shouldStop; + } + } + + /** + * Creates the URI of the update file for the given edition. + * + * @param edition + * The edition number + * @return The URI for the update file for the given edition + */ + private String constructUpdateKey(int edition) { + return UPDATE_KEY + "/jSite/" + edition + "/jSite.properties"; + } + + // + // INTERFACE Runnable + // + + /** + * {@inheritDoc} + */ + public void run() { + Connection connection = freenetInterface.getConnection("jSite-" + ++counter + "-UpdateChecker"); + try { + connection.connect(); + } catch (IOException e1) { + e1.printStackTrace(); + } + Client client = new Client(connection); + boolean checkNow = false; + int currentEdition = lastUpdateEdition; + while (!shouldStop()) { + checkNow = false; + logger.log(Level.FINE, "Trying " + constructUpdateKey(currentEdition)); + ClientGet clientGet = new ClientGet("get-update-key"); + clientGet.setUri(constructUpdateKey(currentEdition)); + clientGet.setPersistence(Persistence.CONNECTION); + clientGet.setReturnType(ReturnType.direct); + clientGet.setVerbosity(Verbosity.ALL); + try { + client.execute(clientGet); + boolean stop = false; + while (!stop) { + Message message = client.readMessage(); + logger.log(Level.FINEST, "Received message: " + message); + if (message == null) { + break; + } + if ("GetFailed".equals(message.getName())) { + if ("27".equals(message.get("code"))) { + String editionString = message.get("redirecturi").split("/")[2]; + int editionNumber = -1; + try { + editionNumber = Integer.parseInt(editionString); + } catch (NumberFormatException nfe1) { + /* ignore. */ + } + if (editionNumber != -1) { + logger.log(Level.INFO, "Found new edition " + editionNumber); + currentEdition = editionNumber; + lastUpdateEdition = editionNumber; + checkNow = true; + break; + } + } + } + if ("AllData".equals(message.getName())) { + logger.log(Level.FINE, "Update data found."); + InputStream dataInputStream = null; + Properties properties = new Properties(); + try { + dataInputStream = message.getPayloadInputStream(); + properties.load(dataInputStream); + } finally { + Closer.close(dataInputStream); + } + + String foundVersionString = properties.getProperty("jSite.Version"); + if (foundVersionString != null) { + Version foundVersion = Version.parse(foundVersionString); + if (foundVersion != null) { + lastVersion = foundVersion; + String versionTimestampString = properties.getProperty("jSite.Date"); + logger.log(Level.FINEST, "Version timestamp: " + versionTimestampString); + long versionTimestamp = -1; + try { + versionTimestamp = Long.parseLong(versionTimestampString); + } catch (NumberFormatException nfe1) { + /* ignore. */ + } + fireUpdateFound(foundVersion, versionTimestamp); + stop = true; + checkNow = true; + ++currentEdition; + } + } + } + } + } catch (IOException e) { + logger.log(Level.INFO, "Got IOException: " + e.getMessage()); + e.printStackTrace(); + } + if (!checkNow && !shouldStop()) { + synchronized (syncObject) { + try { + syncObject.wait(15 * 60 * 1000); + } catch (InterruptedException ie1) { + /* ignore. */ + } + } + } + } + } + +} diff --git a/src/main/java/de/todesbaum/jsite/application/UpdateListener.java b/src/main/java/de/todesbaum/jsite/application/UpdateListener.java new file mode 100644 index 0000000..a37440c --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/application/UpdateListener.java @@ -0,0 +1,44 @@ +/* + * jSite - UpdateListener.java - Copyright © 2008–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.application; + +import java.util.EventListener; + +import de.todesbaum.jsite.main.Version; + +/** + * Listener interface for objects that want to be notified when update data was + * found. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public interface UpdateListener extends EventListener { + + /** + * Notifies a listener that data for the given version was found. + * + * @param foundVersion + * The version that was found + * @param versionTimestamp + * The timestamp of the version, or -1 if the + * timestamp is unknown + */ + public void foundUpdateData(Version foundVersion, long versionTimestamp); + +} diff --git a/src/main/java/de/todesbaum/jsite/gui/FileScanner.java b/src/main/java/de/todesbaum/jsite/gui/FileScanner.java new file mode 100644 index 0000000..1790fbf --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/FileScanner.java @@ -0,0 +1,344 @@ +/* + * jSite - FileScanner.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.util.io.Closer; +import de.todesbaum.util.io.StreamCopier; + +/** + * Scans the local path of a project anychronously and returns the list of found + * files as an event. + * + * @see Project#getLocalPath() + * @see FileScannerListener#fileScannerFinished(FileScanner) + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +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(); + + /** The project to scan. */ + private final Project project; + + /** The list of found files. */ + private List files; + + /** Wether there was an error. */ + private boolean error = false; + + /** + * Creates a new file scanner for the given project. + * + * @param project + * The project whose files to scan + */ + public FileScanner(Project project) { + this.project = project; + } + + /** + * Adds the given listener to the list of listeners. + * + * @param fileScannerListener + * The listener to add + */ + public void addFileScannerListener(FileScannerListener fileScannerListener) { + fileScannerListeners.add(fileScannerListener); + } + + /** + * Removes the given listener from the list of listeners. + * + * @param fileScannerListener + * The listener to remove + */ + public void removeFileScannerListener(FileScannerListener fileScannerListener) { + fileScannerListeners.remove(fileScannerListener); + } + + /** + * Notifies all listeners that the file scan finished. + */ + protected void fireFileScannerFinished() { + for (FileScannerListener fileScannerListener : new ArrayList(fileScannerListeners)) { + fileScannerListener.fileScannerFinished(this); + } + } + + /** + * {@inheritDoc} + *

+ * Scans all available files in the project’s local path and emits an event + * when finished. + * + * @see FileScannerListener#fileScannerFinished(FileScanner) + */ + public void run() { + files = new ArrayList(); + error = false; + try { + scanFiles(new File(project.getLocalPath()), files); + Collections.sort(files); + } catch (IOException ioe1) { + error = true; + } + fireFileScannerFinished(); + } + + /** + * Returns whether there was an error scanning for files. + * + * @return true if there was an error, false + * otherwise + */ + public boolean isError() { + return error; + } + + /** + * Returns the list of found files. + * + * @return The list of found files + */ + public List getFiles() { + return files; + } + + /** + * Recursively scans a directory and adds all found files to the given list. + * + * @param rootDir + * The directory to scan + * @param fileList + * The list to which to add the found files + * @throws IOException + * if an I/O error occurs + */ + private void scanFiles(File rootDir, List fileList) throws IOException { + File[] files = rootDir.listFiles(new FileFilter() { + + @SuppressWarnings("synthetic-access") + public boolean accept(File file) { + return !project.isIgnoreHiddenFiles() || !file.isHidden(); + } + }); + if (files == null) { + throw new IOException(I18n.getMessage("jsite.file-scanner.can-not-read-directory")); + } + for (File file : files) { + if (file.isDirectory()) { + scanFiles(file, fileList); + continue; + } + String filename = project.shortenFilename(file).replace('\\', '/'); + String hash = hashFile(project.getLocalPath(), filename); + fileList.add(new ScannedFile(filename, hash)); + } + } + + /** + * Hashes the given file. + * + * @param path + * The path of the project + * @param filename + * The name of the file, relative to the project path + * @return The hash of the file + */ + @SuppressWarnings("synthetic-access") + private static String hashFile(String path, String filename) { + InputStream fileInputStream = null; + DigestOutputStream digestOutputStream = null; + File file = new File(path, filename); + try { + fileInputStream = new FileInputStream(file); + digestOutputStream = new DigestOutputStream(new NullOutputStream(), MessageDigest.getInstance("SHA-256")); + StreamCopier.copy(fileInputStream, digestOutputStream, file.length()); + return toHex(digestOutputStream.getMessageDigest().digest()); + } catch (NoSuchAlgorithmException nsae1) { + logger.log(Level.WARNING, "Could not get SHA-256 digest!", nsae1); + } catch (IOException ioe1) { + logger.log(Level.WARNING, "Could not read file!", ioe1); + } finally { + Closer.close(digestOutputStream); + Closer.close(fileInputStream); + } + return toHex(new byte[32]); + } + + /** + * Converts the given byte array into a hexadecimal string. + * + * @param array + * The array to convert + * @return The hexadecimal string + */ + private static String toHex(byte[] array) { + StringBuilder hexString = new StringBuilder(array.length * 2); + for (byte b : array) { + hexString.append("0123456789abcdef".charAt((b >>> 4) & 0x0f)).append("0123456789abcdef".charAt(b & 0xf)); + } + return hexString.toString(); + } + + /** + * {@link OutputStream} that discards all written bytes. + * + * @author David ‘Bombe’ Roden <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); + } + + } + +} diff --git a/src/main/java/de/todesbaum/jsite/gui/FileScannerListener.java b/src/main/java/de/todesbaum/jsite/gui/FileScannerListener.java new file mode 100644 index 0000000..63e2c3d --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/FileScannerListener.java @@ -0,0 +1,40 @@ +/* + * jSite - FileScannerListener.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.util.EventListener; + +/** + * Listener interface for objects that want to be notified when scanning a + * project’s local path has finished. + * + * @see FileScanner + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public interface FileScannerListener extends EventListener { + + /** + * Notifies a listener that scanning a project’s local path has finished. + * + * @param fileScanner + * The file scanner that finished + */ + public void fileScannerFinished(FileScanner fileScanner); + +} \ No newline at end of file diff --git a/src/main/java/de/todesbaum/jsite/gui/NodeManagerListener.java b/src/main/java/de/todesbaum/jsite/gui/NodeManagerListener.java new file mode 100644 index 0000000..760c967 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/NodeManagerListener.java @@ -0,0 +1,49 @@ +/* + * jSite - NodeManagerListener.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.util.EventListener; + +import de.todesbaum.jsite.application.Node; + +/** + * Listener interface for objects that want to be notified if the node + * configuration changes. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public interface NodeManagerListener extends EventListener { + + /** + * Notifies a listener that the node configuration was changed. + * + * @param nodes + * The new list of nodes + */ + public void nodesUpdated(Node[] nodes); + + /** + * Notifies a listener that the selected node has changed. + * + * @param node + * The new selected node + */ + public void nodeSelected(Node node); + +} diff --git a/src/main/java/de/todesbaum/jsite/gui/NodeManagerPage.java b/src/main/java/de/todesbaum/jsite/gui/NodeManagerPage.java new file mode 100644 index 0000000..8e44129 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/NodeManagerPage.java @@ -0,0 +1,450 @@ +/* + * jSite - NodeManagerPage.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; + +import de.todesbaum.jsite.application.Node; +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.util.swing.TLabel; +import de.todesbaum.util.swing.TWizard; +import de.todesbaum.util.swing.TWizardPage; + +/** + * Wizard page that lets the user edit his nodes. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class NodeManagerPage extends TWizardPage implements ListSelectionListener, DocumentListener, ChangeListener { + + /** List of node manager listeners. */ + private List nodeManagerListeners = new ArrayList(); + + /** The “add node” action. */ + protected Action addNodeAction; + + /** The “delete node” action. */ + protected Action deleteNodeAction; + + /** The node list model. */ + private DefaultListModel nodeListModel; + + /** The node list. */ + private JList nodeList; + + /** The node name textfield. */ + private JTextField nodeNameTextField; + + /** The node hostname textfield. */ + private JTextField nodeHostnameTextField; + + /** The spinner for the node port. */ + private JSpinner nodePortSpinner; + + /** + * Creates a new node manager wizard page. + * + * @param wizard + * The wizard this page belongs to + */ + public NodeManagerPage(final TWizard wizard) { + super(wizard); + pageInit(); + setHeading(I18n.getMessage("jsite.node-manager.heading")); + setDescription(I18n.getMessage("jsite.node-manager.description")); + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + setHeading(I18n.getMessage("jsite.node-manager.heading")); + setDescription(I18n.getMessage("jsite.node-manager.description")); + } + }); + } + + /** + * Adds a listener for node manager events. + * + * @param nodeManagerListener + * The listener to add + */ + public void addNodeManagerListener(NodeManagerListener nodeManagerListener) { + nodeManagerListeners.add(nodeManagerListener); + } + + /** + * Removes a listener for node manager events. + * + * @param nodeManagerListener + * The listener to remove + */ + public void removeNodeManagerListener(NodeManagerListener nodeManagerListener) { + nodeManagerListeners.remove(nodeManagerListener); + } + + /** + * Notifies all listeners that the node configuration has changed. + * + * @param nodes + * The new list of nodes + */ + protected void fireNodesUpdated(Node[] nodes) { + for (NodeManagerListener nodeManagerListener : nodeManagerListeners) { + nodeManagerListener.nodesUpdated(nodes); + } + } + + /** + * Notifies all listeners that a new node was selected. + * + * @param node + * The newly selected node + */ + protected void fireNodeSelected(Node node) { + for (NodeManagerListener nodeManagerListener : nodeManagerListeners) { + nodeManagerListener.nodeSelected(node); + } + } + + /** + * Creates all actions. + */ + private void createActions() { + addNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.add-node")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + addNode(); + } + }; + + deleteNodeAction = new AbstractAction(I18n.getMessage("jsite.node-manager.delete-node")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + deleteNode(); + } + }; + deleteNodeAction.setEnabled(false); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + addNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.add-node")); + deleteNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.node-manager.delete-node")); + } + }); + } + + /** + * Initializes the page and all components in it. + */ + private void pageInit() { + createActions(); + nodeListModel = new DefaultListModel(); + nodeList = new JList(nodeListModel); + nodeList.setName("node-list"); + nodeList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + nodeList.addListSelectionListener(this); + nodeList.setPreferredSize(new Dimension(250, -1)); + + nodeNameTextField = new JTextField(""); + nodeNameTextField.getDocument().putProperty("Name", "node-name"); + nodeNameTextField.getDocument().addDocumentListener(this); + nodeNameTextField.setEnabled(false); + + nodeHostnameTextField = new JTextField("localhost"); + nodeHostnameTextField.getDocument().putProperty("Name", "node-hostname"); + nodeHostnameTextField.getDocument().addDocumentListener(this); + nodeHostnameTextField.setEnabled(false); + + nodePortSpinner = new JSpinner(new SpinnerNumberModel(9481, 1, 65535, 1)); + nodePortSpinner.setName("node-port"); + nodePortSpinner.addChangeListener(this); + nodePortSpinner.setEnabled(false); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12)); + buttonPanel.setBorder(new EmptyBorder(-12, -12, -12, -12)); + buttonPanel.add(new JButton(addNodeAction)); + buttonPanel.add(new JButton(deleteNodeAction)); + + JPanel centerPanel = new JPanel(new BorderLayout()); + JPanel nodeInformationPanel = new JPanel(new GridBagLayout()); + centerPanel.add(nodeInformationPanel, BorderLayout.PAGE_START); + nodeInformationPanel.add(buttonPanel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + final JLabel nodeInformationLabel = new JLabel("" + I18n.getMessage("jsite.node-manager.node-information") + ""); + nodeInformationPanel.add(nodeInformationLabel, new GridBagConstraints(0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 0, 0, 0), 0, 0)); + final TLabel nodeNameLabel = new TLabel(I18n.getMessage("jsite.node-manager.name") + ":", KeyEvent.VK_N, nodeNameTextField); + nodeInformationPanel.add(nodeNameLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + nodeInformationPanel.add(nodeNameTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + final TLabel nodeHostnameLabel = new TLabel(I18n.getMessage("jsite.node-manager.hostname") + ":", KeyEvent.VK_H, nodeHostnameTextField); + nodeInformationPanel.add(nodeHostnameLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + nodeInformationPanel.add(nodeHostnameTextField, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + final TLabel nodePortLabel = new TLabel(I18n.getMessage("jsite.node-manager.port") + ":", KeyEvent.VK_P, nodePortSpinner); + nodeInformationPanel.add(nodePortLabel, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + nodeInformationPanel.add(nodePortSpinner, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0)); + + setLayout(new BorderLayout(12, 12)); + add(new JScrollPane(nodeList), BorderLayout.LINE_START); + add(centerPanel, BorderLayout.CENTER); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + nodeInformationLabel.setText("" + I18n.getMessage("jsite.node-manager.node-information") + ""); + nodeNameLabel.setText(I18n.getMessage("jsite.node-manager.name") + ":"); + nodeHostnameLabel.setText(I18n.getMessage("jsite.node-manager.hostname") + ":"); + nodePortLabel.setText(I18n.getMessage("jsite.node-manager.port") + ":"); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void pageAdded(TWizard wizard) { + this.wizard.setNextEnabled(nodeListModel.getSize() > 0); + this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); + this.wizard.setNextName(I18n.getMessage("jsite.wizard.next")); + this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); + } + + /** + * Sets the node list. + * + * @param nodes + * The list of nodes + */ + public void setNodes(Node[] nodes) { + nodeListModel.clear(); + for (Node node : nodes) { + nodeListModel.addElement(node); + } + nodeList.repaint(); + fireNodesUpdated(nodes); + } + + /** + * Returns the node list. + * + * @return The list of nodes + */ + public Node[] getNodes() { + Node[] returnNodes = new Node[nodeListModel.getSize()]; + for (int nodeIndex = 0, nodeCount = nodeListModel.getSize(); nodeIndex < nodeCount; nodeIndex++) { + returnNodes[nodeIndex] = (Node) nodeListModel.get(nodeIndex); + } + return returnNodes; + } + + /** + * Returns the currently selected node. + * + * @return The selected node, or null if no node is selected + */ + private Node getSelectedNode() { + return (Node) nodeList.getSelectedValue(); + } + + /** + * Updates node name or hostname when the user types into the textfields. + * + * @see #insertUpdate(DocumentEvent) + * @see #removeUpdate(DocumentEvent) + * @see #changedUpdate(DocumentEvent) + * @see DocumentListener + * @param documentEvent + * The document event + */ + private void updateTextField(DocumentEvent documentEvent) { + Node node = getSelectedNode(); + if (node == null) { + return; + } + Document document = documentEvent.getDocument(); + String documentText = null; + try { + documentText = document.getText(0, document.getLength()); + } catch (BadLocationException ble1) { + /* ignore. */ + } + if (documentText == null) { + return; + } + String documentName = (String) document.getProperty("Name"); + if ("node-name".equals(documentName)) { + node.setName(documentText); + nodeList.repaint(); + fireNodesUpdated(getNodes()); + } else if ("node-hostname".equals(documentName)) { + node.setHostname(documentText); + nodeList.repaint(); + fireNodesUpdated(getNodes()); + } + } + + // + // ACTIONS + // + + /** + * Adds a new node to the list of nodes. + */ + private void addNode() { + Node node = new Node("localhost", 9481, I18n.getMessage("jsite.node-manager.new-node")); + nodeListModel.addElement(node); + deleteNodeAction.setEnabled(nodeListModel.size() > 1); + wizard.setNextEnabled(true); + fireNodesUpdated(getNodes()); + } + + /** + * Deletes the currently selected node from the list of nodes. + */ + private void deleteNode() { + Node node = getSelectedNode(); + if (node == null) { + return; + } + if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.node-manager.delete-node.warning"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.CANCEL_OPTION) { + return; + } + int nodeIndex = nodeListModel.indexOf(node); + nodeListModel.removeElement(node); + nodeList.repaint(); + fireNodeSelected((Node) nodeListModel.get(Math.min(nodeIndex, nodeListModel.size() - 1))); + fireNodesUpdated(getNodes()); + deleteNodeAction.setEnabled(nodeListModel.size() > 1); + wizard.setNextEnabled(nodeListModel.size() > 0); + } + + // + // INTERFACE ListSelectionListener + // + + /** + * {@inheritDoc} + */ + @SuppressWarnings("null") + public void valueChanged(ListSelectionEvent e) { + Object source = e.getSource(); + if (source instanceof JList) { + JList sourceList = (JList) source; + if ("node-list".equals(sourceList.getName())) { + Node node = (Node) sourceList.getSelectedValue(); + boolean enabled = (node != null); + nodeNameTextField.setEnabled(enabled); + nodeHostnameTextField.setEnabled(enabled); + nodePortSpinner.setEnabled(enabled); + deleteNodeAction.setEnabled(enabled && (nodeListModel.size() > 1)); + if (enabled) { + nodeNameTextField.setText(node.getName()); + nodeHostnameTextField.setText(node.getHostname()); + nodePortSpinner.setValue(node.getPort()); + } else { + nodeNameTextField.setText(""); + nodeHostnameTextField.setText("localhost"); + nodePortSpinner.setValue(9481); + } + } + } + } + + // + // INTERFACE DocumentListener + // + + /** + * {@inheritDoc} + */ + public void insertUpdate(DocumentEvent e) { + updateTextField(e); + } + + /** + * {@inheritDoc} + */ + public void removeUpdate(DocumentEvent e) { + updateTextField(e); + } + + /** + * {@inheritDoc} + */ + public void changedUpdate(DocumentEvent e) { + updateTextField(e); + } + + // + // INTERFACE ChangeListener + // + + /** + * {@inheritDoc} + */ + public void stateChanged(ChangeEvent e) { + Object source = e.getSource(); + Node selectedNode = getSelectedNode(); + if (selectedNode == null) { + return; + } + if (source instanceof JSpinner) { + JSpinner sourceSpinner = (JSpinner) source; + if ("node-port".equals(sourceSpinner.getName())) { + selectedNode.setPort((Integer) sourceSpinner.getValue()); + fireNodeSelected(selectedNode); + nodeList.repaint(); + } + } + } + +} diff --git a/src/main/java/de/todesbaum/jsite/gui/PreferencesPage.java b/src/main/java/de/todesbaum/jsite/gui/PreferencesPage.java new file mode 100644 index 0000000..5cf258a --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/PreferencesPage.java @@ -0,0 +1,519 @@ +/* + * jSite - PreferencesPage.java - Copyright © 2009–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; + +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; +import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; +import de.todesbaum.util.freenet.fcp2.PriorityClass; +import de.todesbaum.util.swing.TWizard; +import de.todesbaum.util.swing.TWizardPage; + +/** + * Page that shows some preferences that are valid for the complete application. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class PreferencesPage extends TWizardPage { + + /** Select default temp directory action. */ + private Action selectDefaultTempDirectoryAction; + + /** Select custom temp directory action. */ + private Action selectCustomTempDirectoryAction; + + /** Action that chooses a new temp directory. */ + private Action chooseTempDirectoryAction; + + /** Action when selecting “next to JAR file.” */ + private Action nextToJarFileAction; + + /** Action when selecting “home directory.” */ + private Action homeDirectoryAction; + + /** Action when selecting “custom directory.” */ + private Action customDirectoryAction; + + /** Action when selecting “use early encode.” */ + private Action useEarlyEncodeAction; + + /** Action when a priority was selected. */ + private Action priorityAction; + + /** The text field containing the directory. */ + private JTextField tempDirectoryTextField; + + /** The temp directory. */ + private String tempDirectory; + + /** The configuration location. */ + private ConfigurationLocation configurationLocation; + + /** Whether to use “early encode.” */ + private boolean useEarlyEncode; + + /** The prioriy for inserts. */ + private PriorityClass priority; + + /** The “default” button. */ + private JRadioButton defaultTempDirectory; + + /** The “custom” button. */ + private JRadioButton customTempDirectory; + + /** The “next to JAR file” checkbox. */ + private JRadioButton nextToJarFile; + + /** The “home directory” checkbox. */ + private JRadioButton homeDirectory; + + /** The “custom directory” checkbox. */ + private JRadioButton customDirectory; + + /** The “use early encode” checkbox. */ + private JCheckBox useEarlyEncodeCheckBox; + + /** The insert priority select box. */ + private JComboBox insertPriorityComboBox; + + /** The manifest putter select box. */ + private JComboBox manifestPutterComboBox; + + /** + * Creates a new “preferences” page. + * + * @param wizard + * The wizard this page belongs to + */ + public PreferencesPage(TWizard wizard) { + super(wizard); + pageInit(); + setHeading(I18n.getMessage("jsite.preferences.heading")); + setDescription(I18n.getMessage("jsite.preferences.description")); + I18nContainer.getInstance().registerRunnable(new Runnable() { + + /** + * {@inheritDoc} + */ + public void run() { + setHeading(I18n.getMessage("jsite.preferences.heading")); + setDescription(I18n.getMessage("jsite.preferences.description")); + } + }); + } + + // + // ACCESSORS + // + + /** + * Returns the temp directory. + * + * @return The temp directory, or {@code null} to use the default temp + * directory + */ + public String getTempDirectory() { + return tempDirectory; + } + + /** + * Sets the temp directory. + * + * @param tempDirectory + * The temp directory, or {@code null} to use the default temp + * directory + */ + public void setTempDirectory(String tempDirectory) { + this.tempDirectory = tempDirectory; + tempDirectoryTextField.setText((tempDirectory != null) ? tempDirectory : ""); + if (tempDirectory != null) { + customTempDirectory.setSelected(true); + chooseTempDirectoryAction.setEnabled(true); + } else { + defaultTempDirectory.setSelected(true); + } + } + + /** + * Returns the configuration location. + * + * @return The configuration location + */ + public ConfigurationLocation getConfigurationLocation() { + return configurationLocation; + } + + /** + * Sets the configuration location. + * + * @param configurationLocation + * The configuration location + */ + public void setConfigurationLocation(ConfigurationLocation configurationLocation) { + this.configurationLocation = configurationLocation; + switch (configurationLocation) { + case NEXT_TO_JAR_FILE: + nextToJarFile.setSelected(true); + break; + case HOME_DIRECTORY: + homeDirectory.setSelected(true); + break; + case CUSTOM: + customDirectory.setSelected(true); + break; + } + } + + /** + * Sets whether it is possible to select the “next to JAR file” option for + * the configuration location. + * + * @param nextToJarFile + * {@code true} if the configuration file can be saved next to + * the JAR file, {@code false} otherwise + */ + public void setHasNextToJarConfiguration(boolean nextToJarFile) { + this.nextToJarFile.setEnabled(nextToJarFile); + } + + /** + * Sets whether it is possible to select the “custom location” option for + * the configuration location. + * + * @param customDirectory + * {@code true} if the configuration file can be saved to a + * custom location, {@code false} otherwise + */ + public void setHasCustomConfiguration(boolean customDirectory) { + this.customDirectory.setEnabled(customDirectory); + } + + /** + * Returns whether to use the “early encode“ flag for the insert. + * + * @return {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public boolean useEarlyEncode() { + return useEarlyEncode; + } + + /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public void setUseEarlyEncode(boolean useEarlyEncode) { + useEarlyEncodeCheckBox.setSelected(useEarlyEncode); + } + + /** + * Returns the configured insert priority. + * + * @return The insert priority + */ + public PriorityClass getPriority() { + return priority; + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + */ + public void setPriority(PriorityClass priority) { + insertPriorityComboBox.setSelectedItem(priority); + } + + /** + * Returns the selected manifest putter. + * + * @return The selected manifest putter + */ + public ManifestPutter getManifestPutter() { + return (ManifestPutter) manifestPutterComboBox.getSelectedItem(); + } + + /** + * Sets the manifest putter. + * + * @param manifestPutter + * The manifest putter + */ + public void setManifestPutter(ManifestPutter manifestPutter) { + manifestPutterComboBox.setSelectedItem(manifestPutter); + } + + /** + * {@inheritDoc} + */ + @Override + public void pageAdded(TWizard wizard) { + super.pageAdded(wizard); + this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes")); + this.wizard.setNextName(I18n.getMessage("jsite.wizard.next")); + this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); + this.wizard.setNextEnabled(false); + } + + // + // PRIVATE METHODS + // + + /** + * Initializes this page. + */ + private void pageInit() { + createActions(); + setLayout(new BorderLayout(12, 12)); + add(createPreferencesPanel(), BorderLayout.CENTER); + } + + /** + * Creates all actions. + */ + private void createActions() { + selectDefaultTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.default")) { + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + selectDefaultTempDirectory(); + } + }; + selectCustomTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.custom")) { + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + selectCustomTempDirectory(); + } + }; + chooseTempDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.temp-directory.choose")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent e) { + chooseTempDirectory(); + } + }; + nextToJarFileAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.jar")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionevent) { + configurationLocation = ConfigurationLocation.NEXT_TO_JAR_FILE; + } + }; + homeDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.home")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionevent) { + configurationLocation = ConfigurationLocation.HOME_DIRECTORY; + } + }; + customDirectoryAction = new AbstractAction(I18n.getMessage("jsite.preferences.config-directory.custom")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + configurationLocation = ConfigurationLocation.CUSTOM; + } + }; + useEarlyEncodeAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.use-early-encode")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + useEarlyEncode = useEarlyEncodeCheckBox.isSelected(); + } + }; + priorityAction = new AbstractAction(I18n.getMessage("jsite.preferences.insert-options.priority")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + priority = (PriorityClass) insertPriorityComboBox.getSelectedItem(); + } + }; + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + selectDefaultTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.default")); + selectCustomTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.custom")); + chooseTempDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.temp-directory.choose")); + nextToJarFileAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.jar")); + homeDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.home")); + customDirectoryAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.config-directory.custom")); + useEarlyEncodeAction.putValue(Action.NAME, I18n.getMessage("jsite.preferences.insert-options.use-early-encode")); + } + }); + } + + /** + * Creates the panel containing all preferences. + * + * @return The preferences panel + */ + private JPanel createPreferencesPanel() { + JPanel preferencesPanel = new JPanel(new GridBagLayout()); + preferencesPanel.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12)); + + final JLabel tempDirectoryLabel = new JLabel("" + I18n.getMessage("jsite.preferences.temp-directory") + ""); + preferencesPanel.add(tempDirectoryLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0)); + + defaultTempDirectory = new JRadioButton(selectDefaultTempDirectoryAction); + preferencesPanel.add(defaultTempDirectory, new GridBagConstraints(0, 1, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(6, 18, 0, 0), 0, 0)); + + customTempDirectory = new JRadioButton(selectCustomTempDirectoryAction); + preferencesPanel.add(customTempDirectory, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 18, 0, 0), 0, 0)); + + ButtonGroup tempDirectoryButtonGroup = new ButtonGroup(); + defaultTempDirectory.getModel().setGroup(tempDirectoryButtonGroup); + customTempDirectory.getModel().setGroup(tempDirectoryButtonGroup); + + tempDirectoryTextField = new JTextField(); + tempDirectoryTextField.setEditable(false); + if (tempDirectory != null) { + tempDirectoryTextField.setText(tempDirectory); + customTempDirectory.setSelected(true); + } else { + defaultTempDirectory.setSelected(true); + } + chooseTempDirectoryAction.setEnabled(tempDirectory != null); + preferencesPanel.add(tempDirectoryTextField, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); + + JButton chooseButton = new JButton(chooseTempDirectoryAction); + preferencesPanel.add(chooseButton, new GridBagConstraints(2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.BOTH, new Insets(0, 6, 0, 0), 0, 0)); + + final JLabel configurationDirectoryLabel = new JLabel("" + 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() { + + /** + * {@inheritDoc} + */ + 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")); + } + }); + + return preferencesPanel; + } + + /** + * Activates the default temp directory radio button. + */ + private void selectDefaultTempDirectory() { + tempDirectoryTextField.setEnabled(false); + chooseTempDirectoryAction.setEnabled(false); + tempDirectory = null; + } + + /** + * Activates the custom temp directory radio button. + */ + private void selectCustomTempDirectory() { + tempDirectoryTextField.setEnabled(true); + chooseTempDirectoryAction.setEnabled(true); + if (tempDirectoryTextField.getText().length() == 0) { + chooseTempDirectory(); + if (tempDirectoryTextField.getText().length() == 0) { + defaultTempDirectory.setSelected(true); + } + } + } + + /** + * Lets the user choose a new temp directory. + */ + private void chooseTempDirectory() { + JFileChooser fileChooser = new JFileChooser(tempDirectory); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + int returnValue = fileChooser.showDialog(wizard, I18n.getMessage("jsite.preferences.temp-directory.choose.approve")); + if (returnValue == JFileChooser.CANCEL_OPTION) { + return; + } + tempDirectory = fileChooser.getSelectedFile().getPath(); + tempDirectoryTextField.setText(tempDirectory); + } + +} diff --git a/src/main/java/de/todesbaum/jsite/gui/ProjectFilesPage.java b/src/main/java/de/todesbaum/jsite/gui/ProjectFilesPage.java new file mode 100644 index 0000000..80bb52c --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/ProjectFilesPage.java @@ -0,0 +1,577 @@ +/* + * jSite - ProjectFilesPage.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.text.MessageFormat; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; + +import de.todesbaum.jsite.application.FileOption; +import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.gui.FileScanner.ScannedFile; +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.util.mime.DefaultMIMETypes; +import de.todesbaum.util.swing.TLabel; +import de.todesbaum.util.swing.TWizard; +import de.todesbaum.util.swing.TWizardPage; + +/** + * Wizard page that lets the user manage the files of a project. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class ProjectFilesPage extends TWizardPage implements ActionListener, ListSelectionListener, DocumentListener, FileScannerListener { + + /** The project. */ + private Project project; + + /** The “scan files” action. */ + private Action scanAction; + + /** The “ignore hidden files” checkbox. */ + private JCheckBox ignoreHiddenFilesCheckBox; + + /** The list of project files. */ + private JList projectFileList; + + /** The “default file” checkbox. */ + private JCheckBox defaultFileCheckBox; + + /** The “insert” checkbox. */ + private JCheckBox fileOptionsInsertCheckBox; + + /** The “force insert” checkbox. */ + private JCheckBox fileOptionsForceInsertCheckBox; + + /** The “insert redirect” checkbox. */ + private JCheckBox fileOptionsInsertRedirectCheckBox; + + /** The “custom key” textfield. */ + private JTextField fileOptionsCustomKeyTextField; + + /** The “rename” check box. */ + private JCheckBox fileOptionsRenameCheckBox; + + /** The “new name” text field. */ + private JTextField fileOptionsRenameTextField; + + /** The “mime type” combo box. */ + private JComboBox fileOptionsMIMETypeComboBox; + + /** + * Creates a new project file page. + * + * @param wizard + * The wizard the page belongs to + */ + public ProjectFilesPage(final TWizard wizard) { + super(wizard); + pageInit(); + } + + /** + * Initializes the page and all its actions and components. + */ + private void pageInit() { + createActions(); + setLayout(new BorderLayout(12, 12)); + add(createProjectFilesPanel(), BorderLayout.CENTER); + } + + /** + * Creates all actions. + */ + private void createActions() { + scanAction = new AbstractAction(I18n.getMessage("jsite.project-files.action.rescan")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionScan(); + } + }; + scanAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S); + scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip")); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + scanAction.putValue(Action.NAME, I18n.getMessage("jsite.project-files.action.rescan")); + scanAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project-files.action.rescan.tooltip")); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void pageAdded(TWizard wizard) { + actionScan(); + this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); + this.wizard.setNextName(I18n.getMessage("jsite.project-files.insert-now")); + this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); + } + + /** + * Creates the panel contains the project file list and options. + * + * @return The created panel + */ + private JComponent createProjectFilesPanel() { + JPanel projectFilesPanel = new JPanel(new BorderLayout(12, 12)); + + projectFileList = new JList(); + projectFileList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + projectFileList.setMinimumSize(new Dimension(250, projectFileList.getPreferredSize().height)); + projectFileList.addListSelectionListener(this); + + projectFilesPanel.add(new JScrollPane(projectFileList), BorderLayout.CENTER); + + JPanel fileOptionsAlignmentPanel = new JPanel(new BorderLayout(12, 12)); + projectFilesPanel.add(fileOptionsAlignmentPanel, BorderLayout.PAGE_END); + JPanel fileOptionsPanel = new JPanel(new GridBagLayout()); + fileOptionsAlignmentPanel.add(fileOptionsPanel, BorderLayout.PAGE_START); + + ignoreHiddenFilesCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.ignore-hidden-files")); + ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.ignore-hidden-files.tooltip")); + ignoreHiddenFilesCheckBox.setName("ignore-hidden-files"); + ignoreHiddenFilesCheckBox.addActionListener(this); + fileOptionsPanel.add(ignoreHiddenFilesCheckBox, new GridBagConstraints(0, 0, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + + fileOptionsPanel.add(new JButton(scanAction), new GridBagConstraints(0, 1, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); + + final JLabel fileOptionsLabel = new JLabel("" + I18n.getMessage("jsite.project-files.file-options") + ""); + fileOptionsPanel.add(fileOptionsLabel, new GridBagConstraints(0, 2, 5, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 0, 0, 0), 0, 0)); + + defaultFileCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.default")); + defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip")); + defaultFileCheckBox.setName("default-file"); + defaultFileCheckBox.addActionListener(this); + defaultFileCheckBox.setEnabled(false); + + fileOptionsPanel.add(defaultFileCheckBox, new GridBagConstraints(0, 3, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); + + fileOptionsInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.insert"), true); + fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip")); + fileOptionsInsertCheckBox.setName("insert"); + fileOptionsInsertCheckBox.setMnemonic(KeyEvent.VK_I); + fileOptionsInsertCheckBox.addActionListener(this); + fileOptionsInsertCheckBox.setEnabled(false); + + fileOptionsPanel.add(fileOptionsInsertCheckBox, new GridBagConstraints(0, 4, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + + fileOptionsForceInsertCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.force-insert")); + fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip")); + fileOptionsForceInsertCheckBox.setName("force-insert"); + fileOptionsForceInsertCheckBox.setMnemonic(KeyEvent.VK_F); + fileOptionsForceInsertCheckBox.addActionListener(this); + fileOptionsForceInsertCheckBox.setEnabled(false); + + fileOptionsPanel.add(fileOptionsForceInsertCheckBox, new GridBagConstraints(0, 5, 5, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + + fileOptionsCustomKeyTextField = new JTextField(45); + fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip")); + fileOptionsCustomKeyTextField.setEnabled(false); + fileOptionsCustomKeyTextField.getDocument().addDocumentListener(this); + + fileOptionsInsertRedirectCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.insert-redirect"), false); + fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip")); + fileOptionsInsertRedirectCheckBox.setName("insert-redirect"); + fileOptionsInsertRedirectCheckBox.setMnemonic(KeyEvent.VK_R); + fileOptionsInsertRedirectCheckBox.addActionListener(this); + fileOptionsInsertRedirectCheckBox.setEnabled(false); + + final TLabel customKeyLabel = new TLabel(I18n.getMessage("jsite.project-files.custom-key") + ":", KeyEvent.VK_K, fileOptionsCustomKeyTextField); + fileOptionsPanel.add(fileOptionsInsertRedirectCheckBox, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsPanel.add(customKeyLabel, new GridBagConstraints(1, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 6, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsCustomKeyTextField, new GridBagConstraints(2, 6, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + fileOptionsRenameCheckBox = new JCheckBox(I18n.getMessage("jsite.project-files.rename"), false); + fileOptionsRenameCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.rename.tooltip")); + fileOptionsRenameCheckBox.setName("rename"); + fileOptionsRenameCheckBox.setMnemonic(KeyEvent.VK_N); + fileOptionsRenameCheckBox.addActionListener(this); + fileOptionsRenameCheckBox.setEnabled(false); + + fileOptionsRenameTextField = new JTextField(); + fileOptionsRenameTextField.setEnabled(false); + fileOptionsRenameTextField.getDocument().addDocumentListener(new DocumentListener() { + + @SuppressWarnings("synthetic-access") + private void storeText(DocumentEvent documentEvent) { + FileOption fileOption = getSelectedFile(); + if (fileOption == null) { + /* no file selected. */ + return; + } + Document document = documentEvent.getDocument(); + int documentLength = document.getLength(); + try { + fileOption.setChangedName(document.getText(0, documentLength).trim()); + } catch (BadLocationException ble1) { + /* ignore, it should never happen. */ + } + } + + public void changedUpdate(DocumentEvent documentEvent) { + storeText(documentEvent); + } + + public void insertUpdate(DocumentEvent documentEvent) { + storeText(documentEvent); + } + + public void removeUpdate(DocumentEvent documentEvent) { + storeText(documentEvent); + } + + }); + + fileOptionsPanel.add(fileOptionsRenameCheckBox, new GridBagConstraints(0, 7, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsRenameTextField, new GridBagConstraints(2, 7, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + fileOptionsMIMETypeComboBox = new JComboBox(DefaultMIMETypes.getAllMIMETypes()); + fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip")); + fileOptionsMIMETypeComboBox.setName("project-files.mime-type"); + fileOptionsMIMETypeComboBox.addActionListener(this); + fileOptionsMIMETypeComboBox.setEditable(true); + fileOptionsMIMETypeComboBox.setEnabled(false); + + final TLabel mimeTypeLabel = new TLabel(I18n.getMessage("jsite.project-files.mime-type") + ":", KeyEvent.VK_M, fileOptionsMIMETypeComboBox); + fileOptionsPanel.add(mimeTypeLabel, new GridBagConstraints(0, 8, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + fileOptionsPanel.add(fileOptionsMIMETypeComboBox, new GridBagConstraints(1, 8, 4, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + ignoreHiddenFilesCheckBox.setText(I18n.getMessage("jsite.project-files.ignore-hidden-files")); + ignoreHiddenFilesCheckBox.setToolTipText(I18n.getMessage("jsite.projet-files.ignore-hidden-files.tooltip")); + fileOptionsLabel.setText("" + I18n.getMessage("jsite.project-files.file-options") + ""); + defaultFileCheckBox.setText(I18n.getMessage("jsite.project-files.default")); + defaultFileCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.default.tooltip")); + fileOptionsInsertCheckBox.setText(I18n.getMessage("jsite.project-files.insert")); + fileOptionsInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert.tooltip")); + fileOptionsForceInsertCheckBox.setText(I18n.getMessage("jsite.project-files.force-insert")); + fileOptionsForceInsertCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.force-insert.tooltip")); + fileOptionsInsertRedirectCheckBox.setText(I18n.getMessage("jsite.project-files.insert-redirect")); + fileOptionsInsertRedirectCheckBox.setToolTipText(I18n.getMessage("jsite.project-files.insert-redirect.tooltip")); + fileOptionsCustomKeyTextField.setToolTipText(I18n.getMessage("jsite.project-files.custom-key.tooltip")); + customKeyLabel.setText(I18n.getMessage("jsite.project-files.custom-key") + ":"); + fileOptionsRenameCheckBox.setText("jsite.project-files.rename"); + fileOptionsRenameCheckBox.setToolTipText("jsite.project-files.rename.tooltip"); + fileOptionsMIMETypeComboBox.setToolTipText(I18n.getMessage("jsite.project-files.mime-type.tooltip")); + mimeTypeLabel.setText(I18n.getMessage("jsite.project-files.mime-type") + ":"); + } + }); + + return projectFilesPanel; + } + + /** + * Sets the project whose files to manage. + * + * @param project + * The project whose files to manage + */ + public void setProject(final Project project) { + this.project = project; + setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName())); + setDescription(I18n.getMessage("jsite.project-files.description")); + ignoreHiddenFilesCheckBox.setSelected(project.isIgnoreHiddenFiles()); + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + setHeading(MessageFormat.format(I18n.getMessage("jsite.project-files.heading"), project.getName())); + setDescription(I18n.getMessage("jsite.project-files.description")); + } + }); + } + + // + // ACTIONS + // + + /** + * Rescans the project’s files. + */ + private void actionScan() { + projectFileList.clearSelection(); + projectFileList.setListData(new Object[0]); + + wizard.setNextEnabled(false); + wizard.setPreviousEnabled(false); + wizard.setQuitEnabled(false); + + FileScanner fileScanner = new FileScanner(project); + fileScanner.addFileScannerListener(this); + new Thread(fileScanner).start(); + } + + /** + * {@inheritDoc} + *

+ * Updates the file list. + */ + public void fileScannerFinished(FileScanner fileScanner) { + final boolean error = fileScanner.isError(); + if (!error) { + final List files = fileScanner.getFiles(); + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + projectFileList.setListData(files.toArray()); + projectFileList.clearSelection(); + } + }); + Set entriesToRemove = new HashSet(); + Iterator filenames = new HashSet(project.getFileOptions().keySet()).iterator(); + while (filenames.hasNext()) { + String filename = filenames.next(); + boolean found = false; + for (ScannedFile scannedFile : files) { + if (scannedFile.getFilename().equals(filename)) { + found = true; + break; + } + } + if (!found) { + entriesToRemove.add(filename); + } + } + for (String filename : entriesToRemove) { + project.setFileOption(filename, null); + } + } else { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.scan-error"), null, JOptionPane.ERROR_MESSAGE); + } + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + wizard.setPreviousEnabled(true); + wizard.setNextEnabled(!error); + wizard.setQuitEnabled(true); + } + }); + } + + /** + * Returns the {@link FileOption file options} for the currently selected + * file. + * + * @return The {@link FileOption}s for the selected file, or {@code null} if + * no file is selected + */ + private FileOption getSelectedFile() { + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + if (scannedFile == null) { + return null; + } + return project.getFileOption(scannedFile.getFilename()); + } + + // + // INTERFACE ActionListener + // + + /** + * {@inheritDoc} + */ + public void actionPerformed(ActionEvent actionEvent) { + Object source = actionEvent.getSource(); + if ((source instanceof JCheckBox) && ("ignore-hidden-files".equals(((JCheckBox) source).getName()))) { + project.setIgnoreHiddenFiles(((JCheckBox) source).isSelected()); + actionScan(); + return; + } + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + if (scannedFile == null) { + return; + } + String filename = scannedFile.getFilename(); + FileOption fileOption = project.getFileOption(filename); + if (source instanceof JCheckBox) { + JCheckBox checkBox = (JCheckBox) source; + if ("default-file".equals(checkBox.getName())) { + if (checkBox.isSelected()) { + if (filename.indexOf('/') > -1) { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project-files.invalid-default-file"), null, JOptionPane.ERROR_MESSAGE); + checkBox.setSelected(false); + } else { + project.setIndexFile(filename); + } + } else { + if (filename.equals(project.getIndexFile())) { + project.setIndexFile(null); + } + } + } else if ("insert".equals(checkBox.getName())) { + boolean isInsert = checkBox.isSelected(); + fileOption.setInsert(isInsert); + fileOptionsInsertRedirectCheckBox.setEnabled(!isInsert); + } else if ("force-insert".equals(checkBox.getName())) { + boolean isForceInsert = checkBox.isSelected(); + fileOption.setForceInsert(isForceInsert); + } else if ("insert-redirect".equals(checkBox.getName())) { + boolean isInsertRedirect = checkBox.isSelected(); + fileOption.setInsertRedirect(isInsertRedirect); + fileOptionsCustomKeyTextField.setEnabled(isInsertRedirect); + } else if ("rename".equals(checkBox.getName())) { + boolean isRenamed = checkBox.isSelected(); + fileOptionsRenameTextField.setEnabled(isRenamed); + fileOption.setChangedName(isRenamed ? fileOptionsRenameTextField.getText() : ""); + } + } else if (source instanceof JComboBox) { + JComboBox comboBox = (JComboBox) source; + if ("project-files.mime-type".equals(comboBox.getName())) { + fileOption.setMimeType((String) comboBox.getSelectedItem()); + } + } + } + + // + // INTERFACE ListSelectionListener + // + + /** + * {@inheritDoc} + */ + @SuppressWarnings("null") + public void valueChanged(ListSelectionEvent e) { + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + boolean enabled = scannedFile != null; + String filename = (scannedFile == null) ? null : scannedFile.getFilename(); + defaultFileCheckBox.setEnabled(enabled); + fileOptionsInsertCheckBox.setEnabled(enabled); + fileOptionsRenameCheckBox.setEnabled(enabled); + fileOptionsMIMETypeComboBox.setEnabled(enabled); + if (filename != null) { + FileOption fileOption = project.getFileOption(filename); + defaultFileCheckBox.setSelected(filename.equals(project.getIndexFile())); + fileOptionsInsertCheckBox.setSelected(fileOption.isInsert()); + fileOptionsForceInsertCheckBox.setEnabled(scannedFile.getHash().equals(fileOption.getLastInsertHash())); + fileOptionsForceInsertCheckBox.setSelected(fileOption.isForceInsert()); + fileOptionsInsertRedirectCheckBox.setEnabled(!fileOption.isInsert()); + fileOptionsInsertRedirectCheckBox.setSelected(fileOption.isInsertRedirect()); + fileOptionsCustomKeyTextField.setEnabled(fileOption.isInsertRedirect()); + fileOptionsCustomKeyTextField.setText(fileOption.getCustomKey()); + fileOptionsRenameCheckBox.setSelected(fileOption.hasChangedName()); + fileOptionsRenameTextField.setEnabled(fileOption.hasChangedName()); + fileOptionsRenameTextField.setText(fileOption.getChangedName()); + fileOptionsMIMETypeComboBox.getModel().setSelectedItem(fileOption.getMimeType()); + } else { + defaultFileCheckBox.setSelected(false); + fileOptionsInsertCheckBox.setSelected(true); + fileOptionsForceInsertCheckBox.setEnabled(false); + fileOptionsForceInsertCheckBox.setSelected(false); + fileOptionsInsertRedirectCheckBox.setEnabled(false); + fileOptionsInsertRedirectCheckBox.setSelected(false); + fileOptionsCustomKeyTextField.setEnabled(false); + fileOptionsCustomKeyTextField.setText("CHK@"); + fileOptionsRenameCheckBox.setEnabled(false); + fileOptionsRenameCheckBox.setSelected(false); + fileOptionsRenameTextField.setEnabled(false); + fileOptionsRenameTextField.setText(""); + fileOptionsMIMETypeComboBox.getModel().setSelectedItem(DefaultMIMETypes.DEFAULT_MIME_TYPE); + } + } + + // + // INTERFACE DocumentListener + // + + /** + * Updates the options of the currently selected file with the changes made + * in the “custom key” textfield. + * + * @param documentEvent + * The document event to process + */ + private void processDocumentUpdate(DocumentEvent documentEvent) { + ScannedFile scannedFile = (ScannedFile) projectFileList.getSelectedValue(); + if (scannedFile == null) { + return; + } + FileOption fileOption = project.getFileOption(scannedFile.getFilename()); + Document document = documentEvent.getDocument(); + try { + String text = document.getText(0, document.getLength()); + fileOption.setCustomKey(text); + } catch (BadLocationException ble1) { + /* ignore. */ + } + } + + /** + * {@inheritDoc} + */ + public void changedUpdate(DocumentEvent documentEvent) { + processDocumentUpdate(documentEvent); + } + + /** + * {@inheritDoc} + */ + public void insertUpdate(DocumentEvent documentEvent) { + processDocumentUpdate(documentEvent); + } + + /** + * {@inheritDoc} + */ + public void removeUpdate(DocumentEvent documentEvent) { + processDocumentUpdate(documentEvent); + } + +} diff --git a/src/main/java/de/todesbaum/jsite/gui/ProjectInsertPage.java b/src/main/java/de/todesbaum/jsite/gui/ProjectInsertPage.java new file mode 100644 index 0000000..bfd8b89 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/ProjectInsertPage.java @@ -0,0 +1,534 @@ +/* + * jSite - ProjectInsertPage.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.awt.BorderLayout; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.text.DateFormat; +import java.text.MessageFormat; +import java.util.Date; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; + +import de.todesbaum.jsite.application.AbortedException; +import de.todesbaum.jsite.application.Freenet7Interface; +import de.todesbaum.jsite.application.InsertListener; +import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.application.ProjectInserter; +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; +import de.todesbaum.util.freenet.fcp2.PriorityClass; +import de.todesbaum.util.io.StreamCopier.ProgressListener; +import de.todesbaum.util.swing.TWizard; +import de.todesbaum.util.swing.TWizardPage; + +/** + * Wizard page that shows the progress of an insert. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class ProjectInsertPage extends TWizardPage implements InsertListener, ClipboardOwner { + + /** The logger. */ + private static final Logger logger = Logger.getLogger(ProjectInsertPage.class.getName()); + + /** The project inserter. */ + private ProjectInserter projectInserter; + + /** The “copy URI” action. */ + private Action copyURIAction; + + /** The “request URI” textfield. */ + private JTextField requestURITextField; + + /** The “start time” label. */ + private JLabel startTimeLabel; + + /** The progress bar. */ + private JProgressBar progressBar; + + /** The start time of the insert. */ + private long startTime = 0; + + /** The number of inserted blocks. */ + private volatile int insertedBlocks; + + /** Whether the “copy URI to clipboard” button was used. */ + private boolean uriCopied; + + /** Whether the insert is currently running. */ + private volatile boolean running = false; + + /** + * Creates a new progress insert wizard page. + * + * @param wizard + * The wizard this page belongs to + */ + public ProjectInsertPage(final TWizard wizard) { + super(wizard); + createActions(); + pageInit(); + setHeading(I18n.getMessage("jsite.insert.heading")); + setDescription(I18n.getMessage("jsite.insert.description")); + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + setHeading(I18n.getMessage("jsite.insert.heading")); + setDescription(I18n.getMessage("jsite.insert.description")); + } + }); + projectInserter = new ProjectInserter(); + projectInserter.addInsertListener(this); + } + + /** + * Creates all used actions. + */ + private void createActions() { + copyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionCopyURI(); + } + }; + copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); + copyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U); + copyURIAction.setEnabled(false); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + copyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri")); + copyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); + } + }); + } + + /** + * Initializes the page. + */ + private void pageInit() { + setLayout(new BorderLayout(12, 12)); + add(createProjectInsertPanel(), BorderLayout.CENTER); + } + + /** + * Creates the main panel. + * + * @return The main panel + */ + private JComponent createProjectInsertPanel() { + JComponent projectInsertPanel = new JPanel(new GridBagLayout()); + + requestURITextField = new JTextField(); + requestURITextField.setEditable(false); + + startTimeLabel = new JLabel(); + + progressBar = new JProgressBar(0, 1); + progressBar.setStringPainted(true); + progressBar.setValue(0); + + final JLabel projectInformationLabel = new JLabel("" + I18n.getMessage("jsite.insert.project-information") + ""); + projectInsertPanel.add(projectInformationLabel, new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + final JLabel requestURILabel = new JLabel(I18n.getMessage("jsite.insert.request-uri") + ":"); + projectInsertPanel.add(requestURILabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); + projectInsertPanel.add(requestURITextField, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + final JLabel startTimeLeftLabel = new JLabel(I18n.getMessage("jsite.insert.start-time") + ":"); + projectInsertPanel.add(startTimeLeftLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); + projectInsertPanel.add(startTimeLabel, new GridBagConstraints(1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + final JLabel progressLabel = new JLabel(I18n.getMessage("jsite.insert.progress") + ":"); + projectInsertPanel.add(progressLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 18, 0, 0), 0, 0)); + projectInsertPanel.add(progressBar, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + projectInsertPanel.add(new JButton(copyURIAction), new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_END, GridBagConstraints.NONE, new Insets(12, 18, 0, 0), 0, 0)); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + projectInformationLabel.setText("" + I18n.getMessage("jsite.insert.project-information") + ""); + requestURILabel.setText(I18n.getMessage("jsite.insert.request-uri") + ":"); + startTimeLeftLabel.setText(I18n.getMessage("jsite.insert.start-time") + ":"); + if (startTime != 0) { + startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date(startTime))); + } else { + startTimeLabel.setText(""); + } + progressLabel.setText(I18n.getMessage("jsite.insert.progress") + ":"); + } + }); + + return projectInsertPanel; + } + + /** + * {@inheritDoc} + */ + @Override + public void pageAdded(TWizard wizard) { + this.wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); + this.wizard.setPreviousEnabled(false); + this.wizard.setNextName(I18n.getMessage("jsite.general.cancel")); + this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); + } + + /** + * Starts the insert. + */ + public void startInsert() { + running = true; + copyURIAction.setEnabled(false); + progressBar.setValue(0); + progressBar.setString(I18n.getMessage("jsite.insert.starting")); + progressBar.setFont(progressBar.getFont().deriveFont(Font.PLAIN)); + projectInserter.start(new ProgressListener() { + + public void onProgress(final long copied, final long length) { + SwingUtilities.invokeLater(new Runnable() { + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public void run() { + int divisor = 1; + while (((copied / divisor) > Integer.MAX_VALUE) || ((length / divisor) > Integer.MAX_VALUE)) { + divisor *= 10; + } + progressBar.setMaximum((int) (length / divisor)); + progressBar.setValue((int) (copied / divisor)); + progressBar.setString("Uploaded: " + copied + " / " + length); + } + }); + } + }); + } + + /** + * Stops the currently running insert. + */ + public void stopInsert() { + if (running) { + wizard.setNextEnabled(false); + projectInserter.stop(); + } + } + + /** + * Returns whether the insert is currently running. + * + * @return {@code true} if the insert is currently running, {@code false} + * otherwise + */ + public boolean isRunning() { + return running; + } + + /** + * Sets the project to insert. + * + * @param project + * The project to insert + */ + public void setProject(final Project project) { + projectInserter.setProject(project); + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + requestURITextField.setText(project.getFinalRequestURI(1)); + } + }); + } + + /** + * Sets the freenet interface to use. + * + * @param freenetInterface + * The freenet interface to use + */ + public void setFreenetInterface(Freenet7Interface freenetInterface) { + projectInserter.setFreenetInterface(freenetInterface); + } + + /** + * Sets the project inserter’s temp directory. + * + * @see ProjectInserter#setTempDirectory(String) + * @param tempDirectory + * The temp directory to use, or {@code null} to use the system + * default + */ + public void setTempDirectory(String tempDirectory) { + projectInserter.setTempDirectory(tempDirectory); + } + + /** + * Returns whether the “copy URI to clipboard” button was used. + * + * @return {@code true} if an URI was copied to clipboard, {@code false} + * otherwise + */ + public boolean wasUriCopied() { + return uriCopied; + } + + /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public void setUseEarlyEncode(boolean useEarlyEncode) { + projectInserter.setUseEarlyEncode(useEarlyEncode); + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + */ + public void setPriority(PriorityClass priority) { + projectInserter.setPriority(priority); + } + + /** + * Sets the manifest putter to use for the insert. + * + * @see ProjectInserter#setManifestPutter(ManifestPutter) + * @param manifestPutter + * The manifest putter + */ + public void setManifestPutter(ManifestPutter manifestPutter) { + projectInserter.setManifestPutter(manifestPutter); + } + + // + // INTERFACE InsertListener + // + + /** + * {@inheritDoc} + */ + public void projectInsertStarted(final Project project) { + + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + startTimeLabel.setText(DateFormat.getDateTimeInstance().format(new Date())); + } + }); + } + + /** + * {@inheritDoc} + */ + public void projectUploadFinished(Project project) { + startTime = System.currentTimeMillis(); + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + progressBar.setString(I18n.getMessage("jsite.insert.starting")); + progressBar.setValue(0); + } + }); + } + + /** + * {@inheritDoc} + */ + public void projectURIGenerated(Project project, final String uri) { + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + copyURIAction.setEnabled(true); + requestURITextField.setText(uri); + } + }); + logger.log(Level.FINEST, "Insert generated URI: " + uri); + int slash = uri.indexOf('/'); + slash = uri.indexOf('/', slash + 1); + int secondSlash = uri.indexOf('/', slash + 1); + if (secondSlash == -1) { + secondSlash = uri.length(); + } + String editionNumber = uri.substring(slash + 1, secondSlash); + logger.log(Level.FINEST, "Extracted edition number: " + editionNumber); + int edition = -1; + try { + edition = Integer.valueOf(editionNumber); + } catch (NumberFormatException nfe1) { + /* ignore. */ + } + logger.log(Level.FINEST, "Insert edition: " + edition + ", Project edition: " + project.getEdition()); + if ((edition != -1) && (edition == project.getEdition())) { + JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.reinserted-edition"), I18n.getMessage("jsite.insert.reinserted-edition.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + + /** + * {@inheritDoc} + */ + public void projectInsertProgress(Project project, final int succeeded, final int failed, final int fatal, final int total, final boolean finalized) { + insertedBlocks = succeeded; + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + if (total == 0) { + return; + } + progressBar.setMaximum(total); + progressBar.setValue(succeeded + failed + fatal); + int progress = (succeeded + failed + fatal) * 100 / total; + StringBuilder progressString = new StringBuilder(); + progressString.append(progress).append("% ("); + progressString.append(succeeded + failed + fatal).append('/').append(total); + progressString.append(") ("); + progressString.append(getTransferRate()); + progressString.append(' ').append(I18n.getMessage("jsite.insert.k-per-s")).append(')'); + progressBar.setString(progressString.toString()); + if (finalized) { + progressBar.setFont(progressBar.getFont().deriveFont(Font.BOLD)); + } + } + }); + } + + /** + * {@inheritDoc} + */ + public void projectInsertFinished(Project project, boolean success, Throwable cause) { + running = false; + if (success) { + String copyURILabel = I18n.getMessage("jsite.insert.okay-copy-uri"); + int selectedValue = JOptionPane.showOptionDialog(this, I18n.getMessage("jsite.insert.inserted"), I18n.getMessage("jsite.insert.done.title"), 0, JOptionPane.INFORMATION_MESSAGE, null, new Object[] { I18n.getMessage("jsite.general.ok"), copyURILabel }, copyURILabel); + if (selectedValue == 1) { + actionCopyURI(); + } + } else { + if (cause == null) { + JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-failed"), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE); + } else { + if (cause instanceof AbortedException) { + JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.insert.insert-aborted"), I18n.getMessage("jsite.insert.insert-aborted.title"), JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.insert.insert-failed-with-cause"), cause.getMessage()), I18n.getMessage("jsite.insert.insert-failed.title"), JOptionPane.ERROR_MESSAGE); + } + } + } + SwingUtilities.invokeLater(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + progressBar.setValue(progressBar.getMaximum()); + progressBar.setString(I18n.getMessage("jsite.insert.done") + " (" + getTransferRate() + " " + I18n.getMessage("jsite.insert.k-per-s") + ")"); + wizard.setNextName(I18n.getMessage("jsite.wizard.next")); + wizard.setNextEnabled(true); + wizard.setQuitEnabled(true); + } + }); + } + + // + // ACTIONS + // + + /** + * Copies the request URI of the project to the clipboard. + */ + private void actionCopyURI() { + uriCopied = true; + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(new StringSelection(requestURITextField.getText()), this); + } + + /** + * Formats the given number so that it always has the the given number of + * fractional digits. + * + * @param number + * The number to format + * @param digits + * The number of fractional digits + * @return The formatted number + */ + private String formatNumber(double number, int digits) { + int multiplier = (int) Math.pow(10, digits); + String formattedNumber = String.valueOf((int) (number * multiplier) / (double) multiplier); + if (formattedNumber.indexOf('.') == -1) { + formattedNumber += '.'; + for (int digit = 0; digit < digits; digit++) { + formattedNumber += "0"; + } + } + return formattedNumber; + } + + /** + * Returns the formatted transfer rate at this point. + * + * @return The formatted transfer rate + */ + private String getTransferRate() { + return formatNumber(insertedBlocks * 32.0 / ((System.currentTimeMillis() - startTime) / 1000), 1); + } + + // + // INTERFACE ClipboardOwner + // + + /** + * {@inheritDoc} + */ + public void lostOwnership(Clipboard clipboard, Transferable contents) { + /* ignore. */ + } + +} diff --git a/src/main/java/de/todesbaum/jsite/gui/ProjectPage.java b/src/main/java/de/todesbaum/jsite/gui/ProjectPage.java new file mode 100644 index 0000000..98ad847 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/gui/ProjectPage.java @@ -0,0 +1,724 @@ +/* + * jSite - ProjectPage.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.ClipboardOwner; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.io.IOException; +import java.text.MessageFormat; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.border.EmptyBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.DocumentFilter; + +import de.todesbaum.jsite.application.Freenet7Interface; +import de.todesbaum.jsite.application.KeyDialog; +import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.util.swing.SortedListModel; +import de.todesbaum.util.swing.TLabel; +import de.todesbaum.util.swing.TWizard; +import de.todesbaum.util.swing.TWizardPage; + +/** + * Wizard page that lets the user manage his projects and start inserts. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class ProjectPage extends TWizardPage implements ListSelectionListener, DocumentListener, ClipboardOwner { + + /** The freenet interface. */ + private Freenet7Interface freenetInterface; + + /** The “browse” action. */ + private Action projectLocalPathBrowseAction; + + /** The “add project” action. */ + private Action projectAddAction; + + /** The “delete project” action. */ + private Action projectDeleteAction; + + /** The “clone project” action. */ + private Action projectCloneAction; + + /** The “manage keys” action. */ + private Action projectManageKeysAction; + + /** The “copy URI” action. */ + private Action projectCopyURIAction; + + /** The “reset edition” action. */ + private Action projectResetEditionAction; + + /** The file chooser. */ + private JFileChooser pathChooser; + + /** The project list model. */ + private SortedListModel projectListModel; + + /** The project list scroll pane. */ + private JScrollPane projectScrollPane; + + /** The project list. */ + private JList projectList; + + /** The project name textfield. */ + private JTextField projectNameTextField; + + /** The project description textfield. */ + private JTextField projectDescriptionTextField; + + /** The local path textfield. */ + private JTextField projectLocalPathTextField; + + /** The textfield for the complete URI. */ + private JTextField projectCompleteUriTextField; + + /** The project path textfield. */ + private JTextField projectPathTextField; + + /** Whether the “copy URI to clipboard” action was used. */ + private boolean uriCopied; + + /** + * Creates a new project page. + * + * @param wizard + * The wizard this page belongs to + */ + public ProjectPage(final TWizard wizard) { + super(wizard); + setLayout(new BorderLayout(12, 12)); + dialogInit(); + setHeading(I18n.getMessage("jsite.project.heading")); + setDescription(I18n.getMessage("jsite.project.description")); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + setHeading(I18n.getMessage("jsite.project.heading")); + setDescription(I18n.getMessage("jsite.project.description")); + } + }); + } + + /** + * Initializes the page. + */ + private void dialogInit() { + createActions(); + + pathChooser = new JFileChooser(); + projectListModel = new SortedListModel(); + projectList = new JList(projectListModel); + projectList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + projectList.addListSelectionListener(this); + + add(projectScrollPane = new JScrollPane(projectList), BorderLayout.LINE_START); + projectScrollPane.setPreferredSize(new Dimension(150, projectList.getPreferredSize().height)); + add(createInformationPanel(), BorderLayout.CENTER); + } + + /** + * {@inheritDoc} + */ + @Override + public void pageAdded(TWizard wizard) { + super.pageAdded(wizard); + projectList.clearSelection(); + this.wizard.setPreviousName(I18n.getMessage("jsite.menu.nodes.manage-nodes")); + this.wizard.setNextName(I18n.getMessage("jsite.wizard.next")); + this.wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); + this.wizard.setNextEnabled(false); + } + + /** + * Adds the given listener to the list of listeners. + * + * @param listener + * The listener to add + */ + public void addListSelectionListener(ListSelectionListener listener) { + projectList.addListSelectionListener(listener); + } + + /** + * Removes the given listener from the list of listeners. + * + * @param listener + * The listener to remove + */ + public void removeListSelectionListener(ListSelectionListener listener) { + projectList.removeListSelectionListener(listener); + } + + /** + * Creates all actions. + */ + private void createActions() { + projectLocalPathBrowseAction = new AbstractAction(I18n.getMessage("jsite.project.action.browse")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionLocalPathBrowse(); + } + }; + projectLocalPathBrowseAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.browse.tooltip")); + projectLocalPathBrowseAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_B); + projectLocalPathBrowseAction.setEnabled(false); + + projectAddAction = new AbstractAction(I18n.getMessage("jsite.project.action.add-project")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionAdd(); + } + }; + projectAddAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.add-project.tooltip")); + projectAddAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_A); + + projectDeleteAction = new AbstractAction(I18n.getMessage("jsite.project.action.delete-project")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionDelete(); + } + }; + projectDeleteAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.delete-project.tooltip")); + projectDeleteAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_D); + projectDeleteAction.setEnabled(false); + + projectCloneAction = new AbstractAction(I18n.getMessage("jsite.project.action.clone-project")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionClone(); + } + }; + projectCloneAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.clone-project.tooltip")); + projectCloneAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_L); + projectCloneAction.setEnabled(false); + + projectCopyURIAction = new AbstractAction(I18n.getMessage("jsite.project.action.copy-uri")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionCopyURI(); + } + }; + projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); + projectCopyURIAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_U); + projectCopyURIAction.setEnabled(false); + + projectManageKeysAction = new AbstractAction(I18n.getMessage("jsite.project.action.manage-keys")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionManageKeys(); + } + }; + projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip")); + projectManageKeysAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_M); + projectManageKeysAction.setEnabled(false); + + projectResetEditionAction = new AbstractAction(I18n.getMessage("jsite.project.action.reset-edition")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + actionResetEdition(); + } + }; + projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip")); + projectResetEditionAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_R); + projectResetEditionAction.setEnabled(false); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + projectLocalPathBrowseAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.browse")); + projectLocalPathBrowseAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.browse.tooltip")); + projectAddAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.add-project")); + projectAddAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.add-project.tooltip")); + projectDeleteAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.delete-project")); + projectDeleteAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.delete-project.tooltip")); + projectCloneAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.clone-project")); + projectCloneAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.clone-project.tooltip")); + projectCopyURIAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.copy-uri")); + projectCopyURIAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.copy-uri.tooltip")); + projectManageKeysAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.manage-keys")); + projectManageKeysAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.manage-keys.tooltip")); + projectResetEditionAction.putValue(Action.NAME, I18n.getMessage("jsite.project.action.reset-edition")); + projectResetEditionAction.putValue(Action.SHORT_DESCRIPTION, I18n.getMessage("jsite.project.action.reset-edition.tooltip")); + pathChooser.setApproveButtonText(I18n.getMessage("jsite.project.action.browse.choose")); + } + }); + } + + /** + * Creates the information panel. + * + * @return The information panel + */ + private JComponent createInformationPanel() { + JPanel informationPanel = new JPanel(new BorderLayout(12, 12)); + + JPanel informationTable = new JPanel(new GridBagLayout()); + + JPanel functionButtons = new JPanel(new FlowLayout(FlowLayout.LEADING, 12, 12)); + functionButtons.setBorder(new EmptyBorder(-12, -12, -12, -12)); + functionButtons.add(new JButton(projectAddAction)); + functionButtons.add(new JButton(projectDeleteAction)); + functionButtons.add(new JButton(projectCloneAction)); + functionButtons.add(new JButton(projectManageKeysAction)); + + informationPanel.add(functionButtons, BorderLayout.PAGE_START); + informationPanel.add(informationTable, BorderLayout.CENTER); + + final JLabel projectInformationLabel = new JLabel("" + I18n.getMessage("jsite.project.project.information") + ""); + informationTable.add(projectInformationLabel, new GridBagConstraints(0, 0, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + + projectNameTextField = new JTextField(); + projectNameTextField.getDocument().putProperty("name", "project.name"); + projectNameTextField.getDocument().addDocumentListener(this); + projectNameTextField.setEnabled(false); + + final TLabel projectNameLabel = new TLabel(I18n.getMessage("jsite.project.project.name") + ":", KeyEvent.VK_N, projectNameTextField); + informationTable.add(projectNameLabel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + informationTable.add(projectNameTextField, new GridBagConstraints(1, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + projectDescriptionTextField = new JTextField(); + projectDescriptionTextField.getDocument().putProperty("name", "project.description"); + projectDescriptionTextField.getDocument().addDocumentListener(this); + projectDescriptionTextField.setEnabled(false); + + final TLabel projectDescriptionLabel = new TLabel(I18n.getMessage("jsite.project.project.description") + ":", KeyEvent.VK_D, projectDescriptionTextField); + informationTable.add(projectDescriptionLabel, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + informationTable.add(projectDescriptionTextField, new GridBagConstraints(1, 2, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + projectLocalPathTextField = new JTextField(); + projectLocalPathTextField.getDocument().putProperty("name", "project.localpath"); + projectLocalPathTextField.getDocument().addDocumentListener(this); + projectLocalPathTextField.setEnabled(false); + + final TLabel projectLocalPathLabel = new TLabel(I18n.getMessage("jsite.project.project.local-path") + ":", KeyEvent.VK_L, projectLocalPathTextField); + informationTable.add(projectLocalPathLabel, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + informationTable.add(projectLocalPathTextField, new GridBagConstraints(1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + informationTable.add(new JButton(projectLocalPathBrowseAction), new GridBagConstraints(2, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + final JLabel projectAddressLabel = new JLabel("" + I18n.getMessage("jsite.project.project.address") + ""); + informationTable.add(projectAddressLabel, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(12, 0, 0, 0), 0, 0)); + + projectPathTextField = new JTextField(); + projectPathTextField.getDocument().putProperty("name", "project.path"); + projectPathTextField.getDocument().addDocumentListener(this); + ((AbstractDocument) projectPathTextField.getDocument()).setDocumentFilter(new DocumentFilter() { + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("synthetic-access") + public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException { + super.insertString(fb, offset, string.replaceAll("/", ""), attr); + updateCompleteURI(); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("synthetic-access") + public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { + super.replace(fb, offset, length, text.replaceAll("/", ""), attrs); + updateCompleteURI(); + } + + /** + * {@inheritDoc} + */ + @Override + @SuppressWarnings("synthetic-access") + public void remove(FilterBypass fb, int offset, int length) throws BadLocationException { + super.remove(fb, offset, length); + updateCompleteURI(); + } + }); + projectPathTextField.setEnabled(false); + + final TLabel projectPathLabel = new TLabel(I18n.getMessage("jsite.project.project.path") + ":", KeyEvent.VK_P, projectPathTextField); + informationTable.add(projectPathLabel, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + informationTable.add(projectPathTextField, new GridBagConstraints(1, 5, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + projectCompleteUriTextField = new JTextField(); + projectCompleteUriTextField.setEditable(false); + final TLabel projectUriLabel = new TLabel(I18n.getMessage("jsite.project.project.uri") + ":", KeyEvent.VK_U, projectCompleteUriTextField); + informationTable.add(projectUriLabel, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(6, 18, 0, 0), 0, 0)); + informationTable.add(projectCompleteUriTextField, new GridBagConstraints(1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + informationTable.add(new JButton(projectCopyURIAction), new GridBagConstraints(2, 6, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(6, 6, 0, 0), 0, 0)); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + public void run() { + projectInformationLabel.setText("" + I18n.getMessage("jsite.project.project.information") + ""); + projectNameLabel.setText(I18n.getMessage("jsite.project.project.name") + ":"); + projectDescriptionLabel.setText(I18n.getMessage("jsite.project.project.description") + ":"); + projectLocalPathLabel.setText(I18n.getMessage("jsite.project.project.local-path") + ":"); + projectAddressLabel.setText("" + I18n.getMessage("jsite.project.project.address") + ""); + projectPathLabel.setText(I18n.getMessage("jsite.project.project.path") + ":"); + projectUriLabel.setText(I18n.getMessage("jsite.project.project.uri") + ":"); + } + }); + + return informationPanel; + } + + /** + * Sets the project list. + * + * @param projects + * The list of projects + */ + public void setProjects(Project[] projects) { + projectListModel.clear(); + for (Project project : projects) { + projectListModel.add(project); + } + } + + /** + * Returns the list of projects. + * + * @return The list of projects + */ + public Project[] getProjects() { + return projectListModel.toArray(new Project[projectListModel.size()]); + } + + /** + * Sets the freenet interface to use. + * + * @param freenetInterface + * The freenetInterface to use + */ + public void setFreenetInterface(Freenet7Interface freenetInterface) { + this.freenetInterface = freenetInterface; + } + + /** + * Returns the currently selected project. + * + * @return The currently selected project + */ + public Project getSelectedProject() { + return (Project) projectList.getSelectedValue(); + } + + /** + * Returns whether the “copy URI to clipboard” button was used. + * + * @return {@code true} if the “copy URI to clipboard” button was used, + * {@code false} otherwise + */ + public boolean wasUriCopied() { + return uriCopied; + } + + /** + * Updates the currently selected project with changed information from a + * textfield. + * + * @param documentEvent + * The document event to process + */ + private void setTextField(DocumentEvent documentEvent) { + Document document = documentEvent.getDocument(); + String propertyName = (String) document.getProperty("name"); + Project project = (Project) projectList.getSelectedValue(); + if (project == null) { + return; + } + try { + String text = document.getText(0, document.getLength()).trim(); + if ("project.name".equals(propertyName)) { + project.setName(text); + projectList.repaint(); + } else if ("project.description".equals(propertyName)) { + project.setDescription(text); + } else if ("project.localpath".equals(propertyName)) { + project.setLocalPath(text); + } else if ("project.privatekey".equals(propertyName)) { + project.setInsertURI(text); + } else if ("project.publickey".equals(propertyName)) { + project.setRequestURI(text); + } else if ("project.path".equals(propertyName)) { + project.setPath(text); + } + } catch (BadLocationException e) { + /* ignore. */ + } + } + + // + // ACTIONS + // + + /** + * Lets the user choose a local path for a project. + */ + private void actionLocalPathBrowse() { + Project project = (Project) projectList.getSelectedValue(); + if (project == null) { + return; + } + pathChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + if (pathChooser.showDialog(this, I18n.getMessage("jsite.project.action.browse.choose")) == JFileChooser.APPROVE_OPTION) { + projectLocalPathTextField.setText(pathChooser.getSelectedFile().getPath()); + } + } + + /** + * Adds a new project. + */ + private void actionAdd() { + String[] keyPair = null; + if (!freenetInterface.hasNode()) { + JOptionPane.showMessageDialog(this, I18n.getMessage("jsite.project-files.no-node-selected"), null, JOptionPane.ERROR_MESSAGE); + return; + } + try { + keyPair = freenetInterface.generateKeyPair(); + } catch (IOException ioe1) { + JOptionPane.showMessageDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.keygen.io-error"), ioe1.getMessage()), null, JOptionPane.ERROR_MESSAGE); + return; + } + Project newProject = new Project(); + newProject.setName(I18n.getMessage("jsite.project.new-project.name")); + newProject.setInsertURI(keyPair[0]); + newProject.setRequestURI(keyPair[1]); + newProject.setEdition(-1); + newProject.setPath(""); + projectListModel.add(newProject); + projectScrollPane.revalidate(); + projectScrollPane.repaint(); + projectList.setSelectedIndex(projectListModel.indexOf(newProject)); + } + + /** + * Deletes the currently selected project. + */ + private void actionDelete() { + int selectedIndex = projectList.getSelectedIndex(); + if (selectedIndex > -1) { + if (JOptionPane.showConfirmDialog(this, MessageFormat.format(I18n.getMessage("jsite.project.action.delete-project.confirm"), ((Project) projectList.getSelectedValue()).getName()), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { + projectListModel.remove(selectedIndex); + projectList.clearSelection(); + if (projectListModel.getSize() != 0) { + projectList.setSelectedIndex(Math.min(selectedIndex, projectListModel.getSize() - 1)); + } + } + } + } + + /** + * Clones the currently selected project. + */ + private void actionClone() { + int selectedIndex = projectList.getSelectedIndex(); + if (selectedIndex > -1) { + Project newProject = new Project((Project) projectList.getSelectedValue()); + newProject.setName(MessageFormat.format(I18n.getMessage("jsite.project.action.clone-project.copy"), newProject.getName())); + projectListModel.add(newProject); + projectList.setSelectedIndex(projectListModel.indexOf(newProject)); + } + } + + /** + * Copies the request URI of the currently selected project to the + * clipboard. + */ + private void actionCopyURI() { + int selectedIndex = projectList.getSelectedIndex(); + if (selectedIndex > -1) { + Project selectedProject = (Project) projectList.getSelectedValue(); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(new StringSelection(selectedProject.getFinalRequestURI(0)), this); + uriCopied = true; + } + } + + /** + * Opens a {@link KeyDialog} and lets the user manipulate the keys of the + * project. + */ + private void actionManageKeys() { + int selectedIndex = projectList.getSelectedIndex(); + if (selectedIndex > -1) { + Project selectedProject = (Project) projectList.getSelectedValue(); + KeyDialog keyDialog = new KeyDialog(freenetInterface, wizard); + keyDialog.setPrivateKey(selectedProject.getInsertURI()); + keyDialog.setPublicKey(selectedProject.getRequestURI()); + keyDialog.setVisible(true); + if (!keyDialog.wasCancelled()) { + String originalPublicKey = selectedProject.getRequestURI(); + String originalPrivateKey = selectedProject.getInsertURI(); + selectedProject.setInsertURI(keyDialog.getPrivateKey()); + selectedProject.setRequestURI(keyDialog.getPublicKey()); + if (!originalPublicKey.equals(selectedProject.getRequestURI()) || !originalPrivateKey.equals(selectedProject.getInsertURI())) { + selectedProject.setEdition(-1); + } + updateCompleteURI(); + } + } + } + + /** + * Resets the edition of the currently selected project. + */ + private void actionResetEdition() { + if (JOptionPane.showConfirmDialog(this, I18n.getMessage("jsite.project.warning.reset-edition"), null, JOptionPane.OK_CANCEL_OPTION) == JOptionPane.CANCEL_OPTION) { + return; + } + int selectedIndex = projectList.getSelectedIndex(); + if (selectedIndex > -1) { + Project selectedProject = (Project) projectList.getSelectedValue(); + selectedProject.setEdition(-1); + updateCompleteURI(); + } + } + + /** + * Updates the complete URI text field. + */ + private void updateCompleteURI() { + int selectedIndex = projectList.getSelectedIndex(); + if (selectedIndex > -1) { + Project selectedProject = (Project) projectList.getSelectedValue(); + projectCompleteUriTextField.setText(selectedProject.getFinalRequestURI(0)); + } + } + + // + // INTERFACE ListSelectionListener + // + + /** + * {@inheritDoc} + */ + public void valueChanged(ListSelectionEvent listSelectionEvent) { + int selectedRow = projectList.getSelectedIndex(); + Project selectedProject = (Project) projectList.getSelectedValue(); + projectNameTextField.setEnabled(selectedRow > -1); + projectDescriptionTextField.setEnabled(selectedRow > -1); + projectLocalPathTextField.setEnabled(selectedRow > -1); + projectPathTextField.setEnabled(selectedRow > -1); + projectLocalPathBrowseAction.setEnabled(selectedRow > -1); + projectDeleteAction.setEnabled(selectedRow > -1); + projectCloneAction.setEnabled(selectedRow > -1); + projectCopyURIAction.setEnabled(selectedRow > -1); + projectManageKeysAction.setEnabled(selectedRow > -1); + projectResetEditionAction.setEnabled(selectedRow > -1); + if (selectedRow > -1) { + projectNameTextField.setText(selectedProject.getName()); + projectDescriptionTextField.setText(selectedProject.getDescription()); + projectLocalPathTextField.setText(selectedProject.getLocalPath()); + projectPathTextField.setText(selectedProject.getPath()); + projectCompleteUriTextField.setText("freenet:" + selectedProject.getFinalRequestURI(0)); + } else { + projectNameTextField.setText(""); + projectDescriptionTextField.setText(""); + projectLocalPathTextField.setText(""); + projectPathTextField.setText(""); + projectCompleteUriTextField.setText(""); + } + } + + // + // INTERFACE ChangeListener + // + + // + // INTERFACE DocumentListener + // + + /** + * {@inheritDoc} + */ + public void insertUpdate(DocumentEvent documentEvent) { + setTextField(documentEvent); + } + + /** + * {@inheritDoc} + */ + public void removeUpdate(DocumentEvent documentEvent) { + setTextField(documentEvent); + } + + /** + * {@inheritDoc} + */ + public void changedUpdate(DocumentEvent documentEvent) { + setTextField(documentEvent); + } + + // + // INTERFACE ClipboardOwner + // + + /** + * {@inheritDoc} + */ + public void lostOwnership(Clipboard clipboard, Transferable contents) { + /* ignore. */ + } + +} diff --git a/src/main/java/de/todesbaum/jsite/i18n/I18n.java b/src/main/java/de/todesbaum/jsite/i18n/I18n.java new file mode 100644 index 0000000..4c1cceb --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/i18n/I18n.java @@ -0,0 +1,103 @@ +/* + * jSite - I18n.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.i18n; + +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Maps i18n keys to translated texts, depending on a current locale. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class I18n { + + /** The default locale, English. */ + private static Locale defaultLocale = new Locale("en"); + + /** The current locale. */ + private static Locale currentLocale; + + /** + * Returns the currently set locale. + * + * @return The current locale + */ + public static Locale getLocale() { + if (currentLocale == null) { + currentLocale = Locale.getDefault(); + } + return currentLocale; + } + + /** + * Sets the current locale. + * + * @param locale + * The new current locale + */ + public static void setLocale(Locale locale) { + currentLocale = locale; + Locale.setDefault(locale); + } + + /** + * Returns the resource bundle for the current locale. + * + * @return The resource bundle for the current locale + */ + public static ResourceBundle getResourceBundle() { + return getResourceBundle(getLocale()); + } + + /** + * Returns the resource bundle for the given locale. + * + * @param locale + * The locale to get the resource bundle for + * @return The resource bundle for the given locale + */ + public static ResourceBundle getResourceBundle(Locale locale) { + return ResourceBundle.getBundle("de.todesbaum.jsite.i18n.jSite", locale); + } + + /** + * Retrieves a translated text for the given i18n key. If the resource + * bundle for the current locale does not have a translation for the given + * key, the default locale is tried. If that fails, the key is returned. + * + * @param key + * The key to get the translation for + * @return The translated value, or the key itself if not translation can be + * found + */ + public static String getMessage(String key) { + try { + return getResourceBundle().getString(key); + } catch (MissingResourceException mre1) { + try { + return getResourceBundle(defaultLocale).getString(key); + } catch (MissingResourceException mre2) { + return key; + } + } + } + +} diff --git a/src/main/java/de/todesbaum/jsite/i18n/I18nContainer.java b/src/main/java/de/todesbaum/jsite/i18n/I18nContainer.java new file mode 100644 index 0000000..da2e0c3 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/i18n/I18nContainer.java @@ -0,0 +1,90 @@ +/* + * jSite - I18nContainer.java - Copyright © 2007–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.i18n; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Container that collects {@link Runnable}s that change the texts of GUI + * components when the current locale has changed. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class I18nContainer implements Iterable { + + /** The container singleton. */ + private static final I18nContainer singleton = new I18nContainer(); + + /** The list of runnables that change texts. */ + private final List i18nRunnables = Collections.synchronizedList(new ArrayList()); + + /** + * The list of runnables that change texts and run after + * {@link #i18nRunnables}. + */ + private final List i18nPostRunnables = Collections.synchronizedList(new ArrayList()); + + /** + * Returns the singleton instance. + * + * @return The singleton instance + */ + public static I18nContainer getInstance() { + return singleton; + } + + /** + * Registers an i18n runnable that is run when the current locale has + * changed. + * + * @param i18nRunnable + * The runnable to register + */ + public void registerRunnable(Runnable i18nRunnable) { + i18nRunnables.add(i18nRunnable); + } + + /** + * Registers a {@link Runnable} that changes texts when the current locale + * has changed and runs after {@link #i18nRunnables} have run. + * + * @param i18nPostRunnable + * The runnable to register + */ + public void registerPostRunnable(Runnable i18nPostRunnable) { + i18nPostRunnables.add(i18nPostRunnable); + } + + /** + * {@inheritDoc} + *

+ * Returns a combined list of {@link #i18nRunnables} and + * {@link #i18nPostRunnables}, in that order. + */ + public Iterator iterator() { + List allRunnables = new ArrayList(); + allRunnables.addAll(i18nRunnables); + allRunnables.addAll(i18nPostRunnables); + return allRunnables.iterator(); + } + +} diff --git a/src/main/java/de/todesbaum/jsite/main/CLI.java b/src/main/java/de/todesbaum/jsite/main/CLI.java new file mode 100644 index 0000000..65c9253 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/main/CLI.java @@ -0,0 +1,297 @@ +/* + * jSite - CLI.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.main; + +import java.io.PrintWriter; + +import de.todesbaum.jsite.application.Freenet7Interface; +import de.todesbaum.jsite.application.InsertListener; +import de.todesbaum.jsite.application.Node; +import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.application.ProjectInserter; +import de.todesbaum.util.io.StreamCopier.ProgressListener; + +/** + * Command-line interface for jSite. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class CLI implements InsertListener { + + /** Object used for synchronization. */ + private Object lockObject = new Object(); + + /** Writer for the console. */ + private PrintWriter outputWriter = new PrintWriter(System.out, true); + + /** The freenet interface. */ + private Freenet7Interface freenetInterface; + + /** The project inserter. */ + private ProjectInserter projectInserter = new ProjectInserter(); + + /** The list of nodes. */ + private Node[] nodes; + + /** The projects. */ + private Project[] projects; + + /** Whether the insert has finished. */ + private boolean finished = false; + + /** Whether the insert finished successfully. */ + private boolean success; + + /** + * Creates a new command-line interface. + * + * @param args + * The command-line arguments + */ + private CLI(String[] args) { + + if ((args.length == 0) || args[0].equals("-h") || args[0].equals("--help")) { + outputWriter.println("\nParameters:\n"); + outputWriter.println(" --config-file="); + outputWriter.println(" --node="); + outputWriter.println(" --project="); + outputWriter.println(" --local-directory="); + outputWriter.println(" --path="); + outputWriter.println(" --edition="); + outputWriter.println("\nA project gets inserted when a new project is loaded on the command line,"); + outputWriter.println("or when the command line is finished. --local-directory, --path, and --edition"); + outputWriter.println("override the parameters in the project."); + return; + } + + String configFile = System.getProperty("user.home") + "/.jSite/config7"; + for (String argument : args) { + String value = argument.substring(argument.indexOf('=') + 1).trim(); + if (argument.startsWith("--config-file=")) { + configFile = value; + } + } + + ConfigurationLocator configurationLocator = new ConfigurationLocator(); + if (configFile != null) { + configurationLocator.setCustomLocation(configFile); + } + Configuration configuration = new Configuration(configurationLocator, configurationLocator.findPreferredLocation()); + + projectInserter.addInsertListener(this); + projects = configuration.getProjects(); + Node node = configuration.getSelectedNode(); + nodes = configuration.getNodes(); + + freenetInterface = new Freenet7Interface(); + freenetInterface.setNode(node); + + projectInserter.setFreenetInterface(freenetInterface); + + Project currentProject = null; + for (String argument : args) { + if (argument.startsWith("--config-file=")) { + /* we already parsed this one. */ + continue; + } + String value = argument.substring(argument.indexOf('=') + 1).trim(); + if (argument.startsWith("--node=")) { + Node newNode = getNode(value); + if (newNode == null) { + outputWriter.println("Node \"" + value + "\" not found."); + return; + } + node = newNode; + freenetInterface.setNode(node); + } else if (argument.startsWith("--project=")) { + if (currentProject != null) { + if (insertProject(currentProject)) { + outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted."); + } else { + outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted."); + } + currentProject = null; + } + currentProject = getProject(value); + if (currentProject == null) { + outputWriter.println("Project \"" + value + "\" not found."); + } + } else if (argument.startsWith("--local-directory")) { + if (currentProject == null) { + outputWriter.println("You can't specifiy --local-directory before --project."); + return; + } + currentProject.setLocalPath(value); + } else if (argument.startsWith("--path=")) { + if (currentProject == null) { + outputWriter.println("You can't specify --path before --project."); + return; + } + currentProject.setPath(value); + } else if (argument.startsWith("--edition=")) { + if (currentProject == null) { + outputWriter.println("You can't specify --edition before --project."); + return; + } + currentProject.setEdition(Integer.parseInt(value)); + } else { + outputWriter.println("Unknown parameter: " + argument); + return; + } + } + + int errorCode = 1; + if (currentProject != null) { + if (insertProject(currentProject)) { + outputWriter.println("Project \"" + currentProject.getName() + "\" successfully inserted."); + errorCode = 0; + } else { + outputWriter.println("Project \"" + currentProject.getName() + "\" was not successfully inserted."); + } + } + + configuration.setProjects(projects); + configuration.save(); + + System.exit(errorCode); + } + + /** + * Returns the project with the given name. + * + * @param name + * The name of the project + * @return The project, or null if no project could be found + */ + private Project getProject(String name) { + for (Project project : projects) { + if (project.getName().equals(name)) { + return project; + } + } + return null; + } + + /** + * Returns the node with the given name. + * + * @param name + * The name of the node + * @return The node, or null if no node could be found + */ + private Node getNode(String name) { + for (Node node : nodes) { + if (node.getName().equals(name)) { + return node; + } + } + return null; + } + + /** + * Inserts the given project. + * + * @param currentProject + * The project to insert + * @return true if the insert finished successfully, + * false otherwise + */ + private boolean insertProject(Project currentProject) { + if (!freenetInterface.hasNode()) { + outputWriter.println("Node is not running!"); + return false; + } + projectInserter.setProject(currentProject); + projectInserter.start(new ProgressListener() { + + public void onProgress(long copied, long length) { + System.out.print("Uploaded: " + copied + " / " + length + " bytes...\r"); + } + }); + synchronized (lockObject) { + while (!finished) { + try { + lockObject.wait(); + } catch (InterruptedException e) { + /* ignore, we're in a loop. */ + } + } + } + return success; + } + + // + // INTERFACE InsertListener + // + + /** + * {@inheritDoc} + */ + public void projectInsertStarted(Project project) { + outputWriter.println("Starting Insert of project \"" + project.getName() + "\"."); + } + + /** + * {@inheritDoc} + */ + public void projectUploadFinished(Project project) { + outputWriter.println("Project \"" + project.getName() + "\" has been uploaded, starting insert..."); + } + + /** + * {@inheritDoc} + */ + public void projectURIGenerated(Project project, String uri) { + outputWriter.println("URI: " + uri); + } + + /** + * {@inheritDoc} + */ + public void projectInsertProgress(Project project, int succeeded, int failed, int fatal, int total, boolean finalized) { + outputWriter.println("Progress: " + succeeded + " done, " + failed + " failed, " + fatal + " fatal, " + total + " total" + (finalized ? " (finalized)" : "") + ", " + ((succeeded + failed + fatal) * 100 / total) + "%"); + } + + /** + * {@inheritDoc} + */ + public void projectInsertFinished(Project project, boolean success, Throwable cause) { + outputWriter.println("Request URI: " + project.getFinalRequestURI(0)); + finished = true; + this.success = success; + synchronized (lockObject) { + lockObject.notify(); + } + } + + // + // MAIN + // + + /** + * Creates a new command-line interface with the given arguments. + * + * @param args + * The command-line arguments + */ + public static void main(String[] args) { + new CLI(args); + } + +} diff --git a/src/main/java/de/todesbaum/jsite/main/Configuration.java b/src/main/java/de/todesbaum/jsite/main/Configuration.java new file mode 100644 index 0000000..f5001b0 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/main/Configuration.java @@ -0,0 +1,645 @@ +/* + * jSite - Configuration.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.main; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.logging.Level; +import java.util.logging.Logger; + +import de.todesbaum.jsite.application.FileOption; +import de.todesbaum.jsite.application.Node; +import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; +import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter; +import de.todesbaum.util.freenet.fcp2.PriorityClass; +import de.todesbaum.util.io.Closer; +import de.todesbaum.util.io.StreamCopier; +import de.todesbaum.util.xml.SimpleXML; +import de.todesbaum.util.xml.XML; + +/** + * The configuration. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class Configuration { + + /** The root node of the configuration. */ + private SimpleXML rootNode; + + /** The configuration locator. */ + private final ConfigurationLocator configurationLocator; + + /** Where the configuration resides. */ + private ConfigurationLocation configurationLocation; + + /** + * Creates a new configuration that is read from the given file. + * + * @param configurationLocator + * The configuration locator + * @param configurationLocation + * The configuration directory + */ + public Configuration(ConfigurationLocator configurationLocator, ConfigurationLocation configurationLocation) { + this.configurationLocator = configurationLocator; + this.configurationLocation = configurationLocation; + readConfiguration(configurationLocator.getFile(configurationLocation)); + } + + // + // ACCESSORS + // + + /** + * Returns the configuration locator. + * + * @return The configuration locator + */ + public ConfigurationLocator getConfigurationLocator() { + return configurationLocator; + } + + /** + * Returns the location the configuration will be written to when calling + * {@link #save()}. + * + * @return The location the configuration will be written to + */ + public ConfigurationLocation getConfigurationDirectory() { + return configurationLocation; + } + + /** + * Sets the location the configuration will be written to when calling + * {@link #save()}. + * + * @param configurationLocation + * The location to write the configuration to + */ + public void setConfigurationLocation(ConfigurationLocation configurationLocation) { + this.configurationLocation = configurationLocation; + } + + /** + * Reads the configuration from the file. + * + * @param filename + * The name of the file to read the configuration from + */ + private void readConfiguration(String filename) { + Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Reading configuration from: " + filename); + if (filename != null) { + File configurationFile = new File(filename); + if (configurationFile.exists()) { + ByteArrayOutputStream fileByteOutputStream = null; + FileInputStream fileInputStream = null; + try { + fileByteOutputStream = new ByteArrayOutputStream(); + fileInputStream = new FileInputStream(configurationFile); + StreamCopier.copy(fileInputStream, fileByteOutputStream, configurationFile.length()); + fileByteOutputStream.close(); + byte[] fileBytes = fileByteOutputStream.toByteArray(); + rootNode = SimpleXML.fromDocument(XML.transformToDocument(fileBytes)); + return; + } catch (FileNotFoundException e) { + /* ignore. */ + } catch (IOException e) { + /* ignore. */ + } finally { + Closer.close(fileInputStream); + Closer.close(fileByteOutputStream); + } + } + } + rootNode = new SimpleXML("configuration"); + } + + /** + * Saves the configuration. + * + * @return true if the configuration could be saved, + * false otherwise + */ + public boolean save() { + Logger.getLogger(Configuration.class.getName()).log(Level.CONFIG, "Trying to save configuration to: " + configurationLocation); + File configurationFile = new File(configurationLocator.getFile(configurationLocation)); + if (!configurationFile.exists()) { + File configurationFilePath = configurationFile.getAbsoluteFile().getParentFile(); + if (!configurationFilePath.exists() && !configurationFilePath.mkdirs()) { + return false; + } + } + FileOutputStream fileOutputStream = null; + ByteArrayInputStream configurationInputStream = null; + try { + byte[] configurationBytes = XML.transformToByteArray(rootNode.getDocument()); + configurationInputStream = new ByteArrayInputStream(configurationBytes); + fileOutputStream = new FileOutputStream(configurationFile); + StreamCopier.copy(configurationInputStream, fileOutputStream, configurationBytes.length); + return true; + } catch (IOException ioe1) { + /* ignore. */ + } finally { + Closer.close(configurationInputStream); + Closer.close(fileOutputStream); + } + return false; + } + + /** + * Returns the value of a node. + * + * @param nodeNames + * The name of all nodes in the chain + * @param defaultValue + * The default value to return if the node could not be found + * @return The value of the node, or the default value if the node could not + * be found + */ + private String getNodeValue(String[] nodeNames, String defaultValue) { + SimpleXML node = rootNode; + int nodeIndex = 0; + while ((node != null) && (nodeIndex < nodeNames.length)) { + node = node.getNode(nodeNames[nodeIndex++]); + } + if (node == null) { + return defaultValue; + } + return node.getValue(); + } + + /** + * Returns the integer value of a node. + * + * @param nodeNames + * The names of all nodes in the chain + * @param defaultValue + * The default value to return if the node can not be found + * @return The parsed integer value, or the default value if the node can + * not be found or the value can not be parsed into an integer + */ + private int getNodeIntValue(String[] nodeNames, int defaultValue) { + try { + return Integer.parseInt(getNodeValue(nodeNames, String.valueOf(defaultValue))); + } catch (NumberFormatException nfe1) { + /* ignore. */ + } + return defaultValue; + } + + /** + * Returns the boolean value of a node. + * + * @param nodeNames + * The names of all nodes in the chain + * @param defaultValue + * The default value to return if the node can not be found + * @return The parsed boolean value, or the default value if the node can + * not be found + */ + private boolean getNodeBooleanValue(String[] nodeNames, boolean defaultValue) { + String nodeValue = getNodeValue(nodeNames, null); + if (nodeValue == null) { + return defaultValue; + } + return Boolean.parseBoolean(nodeValue); + } + + /** + * Returns the hostname of the node. + * + * @return The hostname of the node + * @deprecated Use {@link #getSelectedNode()} instead + */ + @Deprecated + public String getNodeAddress() { + return getNodeValue(new String[] { "node-address" }, "localhost"); + } + + /** + * Sets the hostname of the node. + * + * @param nodeAddress + * The hostname of the node + * @deprecated Use {@link #setSelectedNode(Node)} instead + */ + @Deprecated + public void setNodeAddress(String nodeAddress) { + rootNode.replace("node-address", nodeAddress); + } + + /** + * The port number of the node + * + * @return The port number of the node + * @deprecated Use {@link #getSelectedNode()} instead. + */ + @Deprecated + public int getNodePort() { + return getNodeIntValue(new String[] { "node-port" }, 9481); + } + + /** + * Sets the port number of the node. + * + * @param nodePort + * The port number of the node + * @deprecated Use {@link #setSelectedNode(Node)} instead + */ + @Deprecated + public void setNodePort(int nodePort) { + rootNode.replace("node-port", String.valueOf(nodePort)); + } + + /** + * Returns whether the node configuration page should be skipped on startup. + * + * @return true to skip the node configuration page on startup, + * false to show it + */ + public boolean isSkipNodePage() { + return getNodeBooleanValue(new String[] { "skip-node-page" }, false); + } + + /** + * Sets whether the node configuration page should be skipped on startup. + * + * @param skipNodePage + * true to skip the node configuration page on + * startup, false to show it + */ + public void setSkipNodePage(boolean skipNodePage) { + rootNode.replace("skip-node-page", String.valueOf(skipNodePage)); + } + + /** + * Returns all configured projects. + * + * @return A list of all projects + */ + public Project[] getProjects() { + List projects = new ArrayList(); + SimpleXML projectsNode = rootNode.getNode("project-list"); + if (projectsNode != null) { + SimpleXML[] projectNodes = projectsNode.getNodes("project"); + for (SimpleXML projectNode : projectNodes) { + try { + Project project = new Project(); + projects.add(project); + project.setDescription(projectNode.getNode("description").getValue("")); + String indexFile = projectNode.getNode("index-file").getValue(""); + if (indexFile.indexOf('/') > -1) { + indexFile = ""; + } + project.setIndexFile(indexFile); + project.setLastInsertionTime(Long.parseLong(projectNode.getNode("last-insertion-time").getValue("0"))); + project.setLocalPath(projectNode.getNode("local-path").getValue("")); + project.setName(projectNode.getNode("name").getValue("")); + project.setPath(projectNode.getNode("path").getValue("")); + if ((project.getPath() != null) && (project.getPath().indexOf("/") != -1)) { + project.setPath(project.getPath().replaceAll("/", "")); + } + project.setEdition(Integer.parseInt(projectNode.getNode("edition").getValue("0"))); + project.setInsertURI(projectNode.getNode("insert-uri").getValue("")); + project.setRequestURI(projectNode.getNode("request-uri").getValue("")); + if (projectNode.getNode("ignore-hidden-files") != null) { + project.setIgnoreHiddenFiles(Boolean.parseBoolean(projectNode.getNode("ignore-hidden-files").getValue("true"))); + } else { + project.setIgnoreHiddenFiles(true); + } + + /* load last insert hashes. */ + Map 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) { + String filename = fileOptionNode.getNode("filename").getValue(); + FileOption fileOption = project.getFileOption(filename); + fileOption.setInsert(Boolean.parseBoolean(fileOptionNode.getNode("insert").getValue())); + if (fileOptionNode.getNode("insert-redirect") != null) { + fileOption.setInsertRedirect(Boolean.parseBoolean(fileOptionNode.getNode("insert-redirect").getValue())); + } + fileOption.setCustomKey(fileOptionNode.getNode("custom-key").getValue("")); + if (fileOptionNode.getNode("changed-name") != null) { + fileOption.setChangedName(fileOptionNode.getNode("changed-name").getValue()); + } + fileOption.setMimeType(fileOptionNode.getNode("mime-type").getValue("")); + fileOptions.put(filename, fileOption); + } + } + project.setFileOptions(fileOptions); + } catch (NumberFormatException nfe1) { + nfe1.printStackTrace(); + } + } + } + return projects.toArray(new Project[projects.size()]); + } + + /** + * Sets the list of all projects. + * + * @param projects + * The list of all projects + */ + public void setProjects(Project[] projects) { + SimpleXML projectsNode = new SimpleXML("project-list"); + for (Project project : projects) { + SimpleXML projectNode = projectsNode.append("project"); + projectNode.append("edition", String.valueOf(project.getEdition())); + projectNode.append("description", project.getDescription()); + projectNode.append("index-file", project.getIndexFile()); + projectNode.append("last-insertion-time", String.valueOf(project.getLastInsertionTime())); + projectNode.append("local-path", project.getLocalPath()); + projectNode.append("name", project.getName()); + projectNode.append("path", project.getPath()); + projectNode.append("insert-uri", project.getInsertURI()); + projectNode.append("request-uri", project.getRequestURI()); + projectNode.append("ignore-hidden-files", String.valueOf(project.isIgnoreHiddenFiles())); + + /* store last insert hashes. */ + SimpleXML lastInsertHashesNode = projectNode.append("last-insert-hashes"); + for (Entry 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()) { + Entry entry = entries.next(); + FileOption fileOption = entry.getValue(); + if (fileOption.isCustom()) { + SimpleXML fileOptionNode = fileOptionsNode.append("file-option"); + fileOptionNode.append("filename", entry.getKey()); + fileOptionNode.append("insert", String.valueOf(fileOption.isInsert())); + fileOptionNode.append("insert-redirect", String.valueOf(fileOption.isInsertRedirect())); + fileOptionNode.append("custom-key", fileOption.getCustomKey()); + fileOptionNode.append("changed-name", fileOption.getChangedName()); + fileOptionNode.append("mime-type", fileOption.getMimeType()); + } + } + } + rootNode.replace(projectsNode); + } + + /** + * Returns the stored locale. + * + * @return The stored locale + */ + public Locale getLocale() { + String language = getNodeValue(new String[] { "i18n", "language" }, "en"); + String country = getNodeValue(new String[] { "i18n", "country" }, null); + if (country != null) { + return new Locale(language, country); + } + return new Locale(language); + } + + /** + * Sets the locale to store. + * + * @param locale + * The locale to store + */ + public void setLocale(Locale locale) { + SimpleXML i18nNode = new SimpleXML("i18n"); + if (locale.getCountry().length() != 0) { + i18nNode.append("country", locale.getCountry()); + } + i18nNode.append("language", locale.getLanguage()); + rootNode.replace(i18nNode); + return; + } + + /** + * Returns a list of configured nodes. + * + * @return The list of the configured nodes + */ + public Node[] getNodes() { + SimpleXML nodesNode = rootNode.getNode("nodes"); + if (nodesNode == null) { + String hostname = getNodeAddress(); + int port = getNodePort(); + if (hostname == null) { + hostname = "127.0.0.1"; + port = 9481; + } + return new Node[] { new Node(hostname, port, "Node") }; + } + SimpleXML[] nodeNodes = nodesNode.getNodes("node"); + Node[] returnNodes = new Node[nodeNodes.length]; + int nodeIndex = 0; + for (SimpleXML nodeNode : nodeNodes) { + String name = nodeNode.getNode("name").getValue(); + String hostname = nodeNode.getNode("hostname").getValue(); + int port = Integer.parseInt(nodeNode.getNode("port").getValue()); + Node node = new Node(hostname, port, name); + returnNodes[nodeIndex++] = node; + } + return returnNodes; + } + + /** + * Sets the list of configured nodes. + * + * @param nodes + * The list of configured nodes + */ + public void setNodes(Node[] nodes) { + SimpleXML nodesNode = new SimpleXML("nodes"); + for (Node node : nodes) { + SimpleXML nodeNode = nodesNode.append("node"); + nodeNode.append("name", node.getName()); + nodeNode.append("hostname", node.getHostname()); + nodeNode.append("port", String.valueOf(node.getPort())); + } + rootNode.replace(nodesNode); + rootNode.remove("node-address"); + rootNode.remove("node-port"); + } + + /** + * Sets the selected node. + * + * @param selectedNode + * The selected node + */ + public void setSelectedNode(Node selectedNode) { + SimpleXML selectedNodeNode = new SimpleXML("selected-node"); + selectedNodeNode.append("name", selectedNode.getName()); + selectedNodeNode.append("hostname", selectedNode.getHostname()); + selectedNodeNode.append("port", String.valueOf(selectedNode.getPort())); + rootNode.replace(selectedNodeNode); + } + + /** + * Returns the selected node. + * + * @return The selected node + */ + public Node getSelectedNode() { + SimpleXML selectedNodeNode = rootNode.getNode("selected-node"); + if (selectedNodeNode == null) { + String hostname = getNodeAddress(); + int port = getNodePort(); + if (hostname == null) { + hostname = "127.0.0.1"; + port = 9481; + } + return new Node(hostname, port, "Node"); + } + String name = selectedNodeNode.getNode("name").getValue(); + String hostname = selectedNodeNode.getNode("hostname").getValue(); + int port = Integer.valueOf(selectedNodeNode.getNode("port").getValue()); + return new Node(hostname, port, name); + } + + /** + * Returns the temp directory to use. + * + * @return The temp directoy, or {@code null} to use the default temp + * directory + */ + public String getTempDirectory() { + return getNodeValue(new String[] { "temp-directory" }, null); + } + + /** + * Sets the temp directory to use. + * + * @param tempDirectory + * The temp directory to use, or {@code null} to use the default + * temp directory + */ + public void setTempDirectory(String tempDirectory) { + if (tempDirectory != null) { + SimpleXML tempDirectoryNode = new SimpleXML("temp-directory"); + tempDirectoryNode.setValue(tempDirectory); + rootNode.replace(tempDirectoryNode); + } else { + rootNode.remove("temp-directory"); + } + } + + /** + * Returns whether to use the “early encode“ flag for the insert. + * + * @return {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + */ + public boolean useEarlyEncode() { + return getNodeBooleanValue(new String[] { "use-early-encode" }, false); + } + + /** + * Sets whether to use the “early encode“ flag for the insert. + * + * @param useEarlyEncode + * {@code true} to set the “early encode” flag for the insert, + * {@code false} otherwise + * @return This configuration + */ + public Configuration setUseEarlyEncode(boolean useEarlyEncode) { + rootNode.replace("use-early-encode", String.valueOf(useEarlyEncode)); + return this; + } + + /** + * Returns the insert priority. + * + * @return The insert priority + */ + public PriorityClass getPriority() { + return PriorityClass.valueOf(getNodeValue(new String[] { "insert-priority" }, "interactive")); + } + + /** + * Sets the insert priority. + * + * @param priority + * The insert priority + * @return This configuration + */ + public Configuration setPriority(PriorityClass priority) { + rootNode.replace("insert-priority", priority.toString()); + return this; + } + + /** + * Returns the manifest putter. + * + * @return The manifest putter + */ + public ManifestPutter getManifestPutter() { + return ManifestPutter.valueOf(getNodeValue(new String[] { "manifest-putter" }, "simple").toUpperCase()); + } + + /** + * Sets the manifest putter. + * + * @param manifestPutter + * The manifest putter + * @return This configuration + */ + public Configuration setManifestPutter(ManifestPutter manifestPutter) { + rootNode.replace("manifest-putter", manifestPutter.name().toLowerCase()); + return this; + } + +} diff --git a/src/main/java/de/todesbaum/jsite/main/ConfigurationLocator.java b/src/main/java/de/todesbaum/jsite/main/ConfigurationLocator.java new file mode 100644 index 0000000..967f936 --- /dev/null +++ b/src/main/java/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/main/java/de/todesbaum/jsite/main/Main.java b/src/main/java/de/todesbaum/jsite/main/Main.java new file mode 100644 index 0000000..edbf5e4 --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/main/Main.java @@ -0,0 +1,753 @@ +/* + * jSite - Main.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.main; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ButtonGroup; +import javax.swing.Icon; +import javax.swing.JList; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import de.todesbaum.jsite.application.Freenet7Interface; +import de.todesbaum.jsite.application.Node; +import de.todesbaum.jsite.application.Project; +import de.todesbaum.jsite.application.ProjectInserter; +import de.todesbaum.jsite.application.ProjectInserter.CheckReport; +import de.todesbaum.jsite.application.ProjectInserter.Issue; +import de.todesbaum.jsite.application.UpdateChecker; +import de.todesbaum.jsite.application.UpdateListener; +import de.todesbaum.jsite.gui.NodeManagerListener; +import de.todesbaum.jsite.gui.NodeManagerPage; +import de.todesbaum.jsite.gui.PreferencesPage; +import de.todesbaum.jsite.gui.ProjectFilesPage; +import de.todesbaum.jsite.gui.ProjectInsertPage; +import de.todesbaum.jsite.gui.ProjectPage; +import de.todesbaum.jsite.i18n.I18n; +import de.todesbaum.jsite.i18n.I18nContainer; +import de.todesbaum.jsite.main.ConfigurationLocator.ConfigurationLocation; +import de.todesbaum.util.image.IconLoader; +import de.todesbaum.util.swing.TWizard; +import de.todesbaum.util.swing.TWizardPage; +import de.todesbaum.util.swing.WizardListener; + +/** + * The main class that ties together everything. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class Main implements ActionListener, ListSelectionListener, WizardListener, NodeManagerListener, UpdateListener { + + /** The logger. */ + private static final Logger logger = Logger.getLogger(Main.class.getName()); + + /** The version. */ + private static final Version VERSION = new Version(0, 10); + + /** The configuration. */ + private Configuration configuration; + + /** The freenet interface. */ + private Freenet7Interface freenetInterface = new Freenet7Interface(); + + /** The update checker. */ + private final UpdateChecker updateChecker; + + /** The jSite icon. */ + private Icon jSiteIcon; + + /** + * Enumeration for all possible pages. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ + private static enum PageType { + + /** The node manager page. */ + PAGE_NODE_MANAGER, + + /** The project page. */ + PAGE_PROJECTS, + + /** The project files page. */ + PAGE_PROJECT_FILES, + + /** The project insert page. */ + PAGE_INSERT_PROJECT, + + /** The preferences page. */ + PAGE_PREFERENCES + + } + + /** The supported locales. */ + private static final Locale[] SUPPORTED_LOCALES = new Locale[] { Locale.ENGLISH, Locale.GERMAN, Locale.FRENCH }; + + /** The actions that switch the language. */ + private Map languageActions = new HashMap(); + + /** The “manage nodes” action. */ + private Action manageNodeAction; + + /** The “preferences” action. */ + private Action optionsPreferencesAction; + + /** The “check for updates” action. */ + private Action checkForUpdatesAction; + + /** The “about jSite” action. */ + private Action aboutAction; + + /** The wizard. */ + private TWizard wizard; + + /** The node menu. */ + private JMenu nodeMenu; + + /** The currently selected node. */ + private Node selectedNode; + + /** Mapping from page type to page. */ + private final Map pages = new HashMap(); + + /** The original location of the configuration file. */ + private ConfigurationLocation originalLocation; + + /** + * Creates a new core with the default configuration file. + */ + private Main() { + this(null); + } + + /** + * Creates a new core with the given configuration from the given file. + * + * @param configFilename + * The name of the configuration file + */ + private Main(String configFilename) { + /* collect all possible configuration file locations. */ + ConfigurationLocator configurationLocator = new ConfigurationLocator(); + if (configFilename != null) { + configurationLocator.setCustomLocation(configFilename); + } + + originalLocation = configurationLocator.findPreferredLocation(); + logger.log(Level.CONFIG, "Using configuration from " + originalLocation + "."); + configuration = new Configuration(configurationLocator, originalLocation); + + Locale.setDefault(configuration.getLocale()); + I18n.setLocale(configuration.getLocale()); + wizard = new TWizard(); + createActions(); + wizard.setJMenuBar(createMenuBar()); + wizard.setQuitName(I18n.getMessage("jsite.wizard.quit")); + wizard.setPreviousEnabled(false); + wizard.setNextEnabled(true); + wizard.addWizardListener(this); + jSiteIcon = IconLoader.loadIcon("/jsite-icon.png"); + wizard.setIcon(jSiteIcon); + + updateChecker = new UpdateChecker(freenetInterface); + updateChecker.addUpdateListener(this); + updateChecker.start(); + + initPages(); + showPage(PageType.PAGE_PROJECTS); + } + + /** + * Creates all actions. + */ + private void createActions() { + for (final Locale locale : SUPPORTED_LOCALES) { + languageActions.put(locale, new AbstractAction(I18n.getMessage("jsite.menu.language." + locale.getLanguage()), IconLoader.loadIcon("/flag-" + locale.getLanguage() + ".png")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + switchLanguage(locale); + } + }); + } + manageNodeAction = new AbstractAction(I18n.getMessage("jsite.menu.nodes.manage-nodes")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + showPage(PageType.PAGE_NODE_MANAGER); + optionsPreferencesAction.setEnabled(true); + wizard.setPreviousName(I18n.getMessage("jsite.wizard.previous")); + wizard.setNextName(I18n.getMessage("jsite.wizard.next")); + } + }; + optionsPreferencesAction = new AbstractAction(I18n.getMessage("jsite.menu.options.preferences")) { + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + optionsPreferences(); + } + }; + checkForUpdatesAction = new AbstractAction(I18n.getMessage("jsite.menu.help.check-for-updates")) { + + /** + * {@inheritDoc} + */ + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent actionEvent) { + showLatestUpdate(); + } + }; + aboutAction = new AbstractAction(I18n.getMessage("jsite.menu.help.about")) { + + @SuppressWarnings("synthetic-access") + public void actionPerformed(ActionEvent e) { + JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.about.message"), getVersion().toString()), null, JOptionPane.INFORMATION_MESSAGE, jSiteIcon); + } + }; + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + manageNodeAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.nodes.manage-nodes")); + optionsPreferencesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.options.preferences")); + checkForUpdatesAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.check-for-updates")); + aboutAction.putValue(Action.NAME, I18n.getMessage("jsite.menu.help.about")); + } + }); + } + + /** + * Creates the menu bar. + * + * @return The menu bar + */ + private JMenuBar createMenuBar() { + JMenuBar menuBar = new JMenuBar(); + final JMenu languageMenu = new JMenu(I18n.getMessage("jsite.menu.languages")); + menuBar.add(languageMenu); + ButtonGroup languageButtonGroup = new ButtonGroup(); + for (Locale locale : SUPPORTED_LOCALES) { + Action languageAction = languageActions.get(locale); + JRadioButtonMenuItem menuItem = new JRadioButtonMenuItem(languageActions.get(locale)); + if (locale.equals(Locale.getDefault())) { + menuItem.setSelected(true); + } + languageAction.putValue("menuItem", menuItem); + languageButtonGroup.add(menuItem); + languageMenu.add(menuItem); + } + nodeMenu = new JMenu(I18n.getMessage("jsite.menu.nodes")); + menuBar.add(nodeMenu); + selectedNode = configuration.getSelectedNode(); + nodesUpdated(configuration.getNodes()); + + final JMenu optionsMenu = new JMenu(I18n.getMessage("jsite.menu.options")); + menuBar.add(optionsMenu); + optionsMenu.add(optionsPreferencesAction); + + /* evil hack to right-align the help menu */ + JPanel panel = new JPanel(); + panel.setOpaque(false); + menuBar.add(panel); + + final JMenu helpMenu = new JMenu(I18n.getMessage("jsite.menu.help")); + menuBar.add(helpMenu); + helpMenu.add(checkForUpdatesAction); + helpMenu.add(aboutAction); + + I18nContainer.getInstance().registerRunnable(new Runnable() { + + @SuppressWarnings("synthetic-access") + public void run() { + languageMenu.setText(I18n.getMessage("jsite.menu.languages")); + nodeMenu.setText(I18n.getMessage("jsite.menu.nodes")); + optionsMenu.setText(I18n.getMessage("jsite.menu.options")); + helpMenu.setText(I18n.getMessage("jsite.menu.help")); + for (Map.Entry languageActionEntry : languageActions.entrySet()) { + languageActionEntry.getValue().putValue(Action.NAME, I18n.getMessage("jsite.menu.language." + languageActionEntry.getKey().getLanguage())); + } + } + }); + + return menuBar; + } + + /** + * Initializes all pages. + */ + private void initPages() { + NodeManagerPage nodeManagerPage = new NodeManagerPage(wizard); + nodeManagerPage.setName("page.node-manager"); + nodeManagerPage.addNodeManagerListener(this); + nodeManagerPage.setNodes(configuration.getNodes()); + pages.put(PageType.PAGE_NODE_MANAGER, nodeManagerPage); + + ProjectPage projectPage = new ProjectPage(wizard); + projectPage.setName("page.project"); + projectPage.setProjects(configuration.getProjects()); + projectPage.setFreenetInterface(freenetInterface); + projectPage.addListSelectionListener(this); + pages.put(PageType.PAGE_PROJECTS, projectPage); + + ProjectFilesPage projectFilesPage = new ProjectFilesPage(wizard); + projectFilesPage.setName("page.project.files"); + pages.put(PageType.PAGE_PROJECT_FILES, projectFilesPage); + + ProjectInsertPage projectInsertPage = new ProjectInsertPage(wizard); + projectInsertPage.setName("page.project.insert"); + projectInsertPage.setFreenetInterface(freenetInterface); + pages.put(PageType.PAGE_INSERT_PROJECT, projectInsertPage); + + PreferencesPage preferencesPage = new PreferencesPage(wizard); + preferencesPage.setName("page.preferences"); + preferencesPage.setTempDirectory(configuration.getTempDirectory()); + pages.put(PageType.PAGE_PREFERENCES, preferencesPage); + } + + /** + * Shows the page with the given type. + * + * @param pageType + * The page type to show + */ + private void showPage(PageType pageType) { + wizard.setPreviousEnabled(pageType.ordinal() > 0); + wizard.setNextEnabled(pageType.ordinal() < (pages.size() - 1)); + wizard.setPage(pages.get(pageType)); + wizard.setTitle(pages.get(pageType).getHeading() + " - jSite"); + } + + /** + * Returns whether a configuration file would be overwritten when calling + * {@link #saveConfiguration()}. + * + * @return {@code true} if {@link #saveConfiguration()} would overwrite an + * existing file, {@code false} otherwise + */ + private boolean isOverwritingConfiguration() { + return configuration.getConfigurationLocator().hasFile(configuration.getConfigurationDirectory()); + } + + /** + * Saves the configuration. + * + * @return true if the configuration could be saved, + * false otherwise + */ + private boolean saveConfiguration() { + NodeManagerPage nodeManagerPage = (NodeManagerPage) pages.get(PageType.PAGE_NODE_MANAGER); + configuration.setNodes(nodeManagerPage.getNodes()); + if (selectedNode != null) { + configuration.setSelectedNode(selectedNode); + } + + ProjectPage projectPage = (ProjectPage) pages.get(PageType.PAGE_PROJECTS); + configuration.setProjects(projectPage.getProjects()); + + PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES); + configuration.setTempDirectory(preferencesPage.getTempDirectory()); + + return configuration.save(); + } + + /** + * Finds a supported locale for the given locale. + * + * @param forLocale + * The locale to find a supported locale for + * @return The supported locale that was found, or the default locale if no + * supported locale could be found + */ + private Locale findSupportedLocale(Locale forLocale) { + for (Locale locale : SUPPORTED_LOCALES) { + if (locale.equals(forLocale)) { + return locale; + } + } + for (Locale locale : SUPPORTED_LOCALES) { + if (locale.getCountry().equals(forLocale.getCountry()) && locale.getLanguage().equals(forLocale.getLanguage())) { + return locale; + } + } + for (Locale locale : SUPPORTED_LOCALES) { + if (locale.getLanguage().equals(forLocale.getLanguage())) { + return locale; + } + } + return SUPPORTED_LOCALES[0]; + } + + /** + * Returns the version. + * + * @return The version + */ + public static final Version getVersion() { + return VERSION; + } + + // + // ACTIONS + // + + /** + * Switches the language of the interface to the given locale. + * + * @param locale + * The locale to switch to + */ + private void switchLanguage(Locale locale) { + Locale supportedLocale = findSupportedLocale(locale); + Action languageAction = languageActions.get(supportedLocale); + JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) languageAction.getValue("menuItem"); + menuItem.setSelected(true); + I18n.setLocale(supportedLocale); + for (Runnable i18nRunnable : I18nContainer.getInstance()) { + try { + i18nRunnable.run(); + } catch (Throwable t) { + /* we probably shouldn't swallow this. */ + } + } + wizard.setPage(wizard.getPage()); + configuration.setLocale(supportedLocale); + } + + /** + * Shows a dialog with general preferences. + */ + private void optionsPreferences() { + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setConfigurationLocation(configuration.getConfigurationDirectory()); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasNextToJarConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.NEXT_TO_JAR_FILE)); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setHasCustomConfiguration(configuration.getConfigurationLocator().isValidLocation(ConfigurationLocation.CUSTOM)); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setUseEarlyEncode(configuration.useEarlyEncode()); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setPriority(configuration.getPriority()); + ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).setManifestPutter(configuration.getManifestPutter()); + showPage(PageType.PAGE_PREFERENCES); + optionsPreferencesAction.setEnabled(false); + wizard.setNextEnabled(true); + wizard.setNextName(I18n.getMessage("jsite.wizard.next")); + } + + /** + * Shows a dialog box that shows the last version that was found by the + * {@link UpdateChecker}. + */ + private void showLatestUpdate() { + Version latestVersion = updateChecker.getLatestVersion(); + int versionDifference = latestVersion.compareTo(VERSION); + if (versionDifference > 0) { + JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.newer.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE); + } else if (versionDifference < 0) { + JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.older.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE); + } else { + JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.latest-version.okay.message"), VERSION, latestVersion), I18n.getMessage("jsite.update-checker.latest-version.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + + // + // INTERFACE ListSelectionListener + // + + /** + * {@inheritDoc} + */ + public void valueChanged(ListSelectionEvent e) { + JList list = (JList) e.getSource(); + int selectedRow = list.getSelectedIndex(); + wizard.setNextEnabled(selectedRow > -1); + } + + // + // INTERFACE WizardListener + // + + /** + * {@inheritDoc} + */ + public void wizardNextPressed(TWizard wizard) { + String pageName = wizard.getPage().getName(); + if ("page.node-manager".equals(pageName)) { + showPage(PageType.PAGE_PROJECTS); + } else if ("page.project".equals(pageName)) { + ProjectPage projectPage = (ProjectPage) wizard.getPage(); + Project project = projectPage.getSelectedProject(); + if ((project.getLocalPath() == null) || (project.getLocalPath().trim().length() == 0)) { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.warning.no-local-path"), null, JOptionPane.ERROR_MESSAGE); + return; + } + if ((project.getPath() == null) || (project.getPath().trim().length() == 0)) { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.warning.no-path"), null, JOptionPane.ERROR_MESSAGE); + return; + } + ((ProjectFilesPage) pages.get(PageType.PAGE_PROJECT_FILES)).setProject(project); + ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).setProject(project); + showPage(PageType.PAGE_PROJECT_FILES); + } else if ("page.project.files".equals(pageName)) { + ProjectPage projectPage = (ProjectPage) pages.get(PageType.PAGE_PROJECTS); + Project project = projectPage.getSelectedProject(); + if (selectedNode == null) { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.error.no-node-selected"), null, JOptionPane.ERROR_MESSAGE); + return; + } + CheckReport checkReport = ProjectInserter.validateProject(project); + for (Issue issue : checkReport) { + if (issue.isFatal()) { + JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.ERROR_MESSAGE); + return; + } + if (JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite." + issue.getErrorKey()), (Object[]) issue.getParameters()), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) != JOptionPane.OK_OPTION) { + return; + } + } + boolean nodeRunning = false; + try { + nodeRunning = freenetInterface.isNodePresent(); + } catch (IOException e) { + /* ignore. */ + } + if (!nodeRunning) { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.error.no-node-running"), null, JOptionPane.ERROR_MESSAGE); + return; + } + configuration.save(); + showPage(PageType.PAGE_INSERT_PROJECT); + ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT); + String tempDirectory = ((PreferencesPage) pages.get(PageType.PAGE_PREFERENCES)).getTempDirectory(); + projectInsertPage.setTempDirectory(tempDirectory); + projectInsertPage.setUseEarlyEncode(configuration.useEarlyEncode()); + projectInsertPage.setPriority(configuration.getPriority()); + projectInsertPage.setManifestPutter(configuration.getManifestPutter()); + projectInsertPage.startInsert(); + nodeMenu.setEnabled(false); + optionsPreferencesAction.setEnabled(false); + } else if ("page.project.insert".equals(pageName)) { + ProjectInsertPage projectInsertPage = (ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT); + if (projectInsertPage.isRunning()) { + projectInsertPage.stopInsert(); + } else { + showPage(PageType.PAGE_PROJECTS); + nodeMenu.setEnabled(true); + optionsPreferencesAction.setEnabled(true); + } + } else if ("page.preferences".equals(pageName)) { + PreferencesPage preferencesPage = (PreferencesPage) pages.get(PageType.PAGE_PREFERENCES); + showPage(PageType.PAGE_PROJECTS); + optionsPreferencesAction.setEnabled(true); + configuration.setUseEarlyEncode(preferencesPage.useEarlyEncode()); + configuration.setPriority(preferencesPage.getPriority()); + configuration.setManifestPutter(preferencesPage.getManifestPutter()); + configuration.setConfigurationLocation(preferencesPage.getConfigurationLocation()); + } + } + + /** + * {@inheritDoc} + */ + public void wizardPreviousPressed(TWizard wizard) { + String pageName = wizard.getPage().getName(); + if ("page.project".equals(pageName) || "page.preferences".equals(pageName)) { + showPage(PageType.PAGE_NODE_MANAGER); + optionsPreferencesAction.setEnabled(true); + } else if ("page.project.files".equals(pageName)) { + showPage(PageType.PAGE_PROJECTS); + } else if ("page.project.insert".equals(pageName)) { + showPage(PageType.PAGE_PROJECT_FILES); + } + } + + /** + * {@inheritDoc} + */ + public void wizardQuitPressed(TWizard wizard) { + if (((ProjectPage) pages.get(PageType.PAGE_PROJECTS)).wasUriCopied() || ((ProjectInsertPage) pages.get(PageType.PAGE_INSERT_PROJECT)).wasUriCopied()) { + JOptionPane.showMessageDialog(wizard, I18n.getMessage("jsite.project.warning.use-clipboard-now")); + } + if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.question"), I18n.getMessage("jsite.quit.question.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) { + if (isOverwritingConfiguration() && !originalLocation.equals(configuration.getConfigurationDirectory())) { + int overwriteConfigurationAnswer = JOptionPane.showConfirmDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.quit.overwrite-configuration"), configuration.getConfigurationLocator().getFile(configuration.getConfigurationDirectory())), I18n.getMessage("jsite.quit.overwrite-configuration.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); + if (overwriteConfigurationAnswer == JOptionPane.YES_OPTION) { + if (saveConfiguration()) { + System.exit(0); + } + } else if (overwriteConfigurationAnswer == JOptionPane.CANCEL_OPTION) { + return; + } + if (overwriteConfigurationAnswer == JOptionPane.NO_OPTION) { + System.exit(0); + } + } else { + if (saveConfiguration()) { + System.exit(0); + } + } + if (JOptionPane.showConfirmDialog(wizard, I18n.getMessage("jsite.quit.config-not-saved"), null, JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION) { + System.exit(0); + } + } + } + + // + // INTERFACE NodeManagerListener + // + + /** + * {@inheritDoc} + */ + public void nodesUpdated(Node[] nodes) { + nodeMenu.removeAll(); + ButtonGroup nodeButtonGroup = new ButtonGroup(); + Node newSelectedNode = null; + for (Node node : nodes) { + JRadioButtonMenuItem nodeMenuItem = new JRadioButtonMenuItem(node.getName()); + nodeMenuItem.putClientProperty("Node", node); + nodeMenuItem.addActionListener(this); + nodeButtonGroup.add(nodeMenuItem); + if (node.equals(selectedNode)) { + newSelectedNode = node; + nodeMenuItem.setSelected(true); + } + nodeMenu.add(nodeMenuItem); + } + nodeMenu.addSeparator(); + nodeMenu.add(manageNodeAction); + selectedNode = newSelectedNode; + freenetInterface.setNode(selectedNode); + } + + /** + * {@inheritDoc} + */ + public void nodeSelected(Node node) { + for (Component menuItem : nodeMenu.getMenuComponents()) { + if (menuItem instanceof JMenuItem) { + if (node.equals(((JMenuItem) menuItem).getClientProperty("Node"))) { + ((JMenuItem) menuItem).setSelected(true); + } + } + } + freenetInterface.setNode(node); + selectedNode = node; + } + + // + // INTERFACE ActionListener + // + + /** + * {@inheritDoc} + */ + public void actionPerformed(ActionEvent e) { + Object source = e.getSource(); + if (source instanceof JRadioButtonMenuItem) { + JRadioButtonMenuItem menuItem = (JRadioButtonMenuItem) source; + Node node = (Node) menuItem.getClientProperty("Node"); + selectedNode = node; + freenetInterface.setNode(selectedNode); + } + } + + // + // INTERFACE UpdateListener + // + + /** + * {@inheritDoc} + */ + public void foundUpdateData(Version foundVersion, long versionTimestamp) { + logger.log(Level.FINEST, "Found version {0} from {1,date}.", new Object[] { foundVersion, versionTimestamp }); + if (foundVersion.compareTo(VERSION) > 0) { + JOptionPane.showMessageDialog(wizard, MessageFormat.format(I18n.getMessage("jsite.update-checker.found-version.message"), foundVersion.toString(), new Date(versionTimestamp)), I18n.getMessage("jsite.update-checker.found-version.title"), JOptionPane.INFORMATION_MESSAGE); + } + } + + // + // MAIN METHOD + // + + /** + * Main method that is called by the VM. + * + * @param args + * The command-line arguments + */ + public static void main(String[] args) { + /* initialize logger. */ + Logger logger = Logger.getLogger("de.todesbaum"); + Handler handler = new ConsoleHandler(); + logger.addHandler(handler); + String configFilename = null; + boolean nextIsConfigFilename = false; + for (String argument : args) { + if (nextIsConfigFilename) { + configFilename = argument; + nextIsConfigFilename = false; + } + if ("--help".equals(argument)) { + printHelp(); + return; + } else if ("--debug".equals(argument)) { + logger.setLevel(Level.ALL); + handler.setLevel(Level.ALL); + } else if ("--config-file".equals(argument)) { + nextIsConfigFilename = true; + } + } + if (nextIsConfigFilename) { + System.out.println("--config-file needs parameter!"); + return; + } + new Main(configFilename); + } + + /** + * Prints a small syntax help. + */ + private static void printHelp() { + System.out.println("--help\tshows this cruft"); + System.out.println("--debug\tenables some debug output"); + System.out.println("--config-file \tuse specified configuration file"); + } + +} diff --git a/src/main/java/de/todesbaum/jsite/main/Version.java b/src/main/java/de/todesbaum/jsite/main/Version.java new file mode 100644 index 0000000..977836c --- /dev/null +++ b/src/main/java/de/todesbaum/jsite/main/Version.java @@ -0,0 +1,113 @@ +/* + * jSite - Version.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.jsite.main; + +/** + * Container for version information. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public class Version implements Comparable { + + /** The components of the version information. */ + private final int[] components; + + /** + * Creates a new version container with the given components. + * + * @param components + * The version components + */ + public Version(int... components) { + this.components = new int[components.length]; + System.arraycopy(components, 0, this.components, 0, components.length); + } + + /** + * Returns the number of version components. + * + * @return The number of version components + */ + public int size() { + return components.length; + } + + /** + * Returns the version component with the given index. + * + * @param index + * The index of the version component + * @return The version component + */ + public int getComponent(int index) { + return components[index]; + } + + /** + * Parses a version from the given string. + * + * @param versionString + * The version string to parse + * @return The parsed version, or null if the string could not + * be parsed + */ + public static Version parse(String versionString) { + String[] componentStrings = versionString.split("\\."); + int[] components = new int[componentStrings.length]; + int index = -1; + for (String componentString : componentStrings) { + try { + components[++index] = Integer.parseInt(componentString); + } catch (NumberFormatException nfe1) { + return null; + } + } + return new Version(components); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + StringBuilder versionString = new StringBuilder(); + for (int component : components) { + if (versionString.length() != 0) { + versionString.append('.'); + } + versionString.append(component); + } + return versionString.toString(); + } + + /** + * {@inheritDoc} + */ + public int compareTo(Version version) { + int lessComponents = Math.min(components.length, version.components.length); + for (int index = 0; index < lessComponents; index++) { + if (version.components[index] == components[index]) { + continue; + } + return components[index] - version.components[index]; + } + return components.length - version.components.length; + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Client.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Client.java new file mode 100644 index 0000000..ede92d1 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/Client.java @@ -0,0 +1,252 @@ +/* + * jSite - Client.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import de.todesbaum.util.io.StreamCopier.ProgressListener; + +/** + * A Client executes {@link Command}s over a {@link Connection} to a + * {@link Node} and delivers resulting {@link Message}s. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class Client implements ConnectionListener { + + /** The connection this client operates on. */ + private final Connection connection; + + /** The identifiers the client filters messages for. */ + private List identifiers = new ArrayList(); + + /** The queued messages. */ + private final List messageQueue = new ArrayList(); + + /** Whether the client was disconnected. */ + private boolean disconnected = false; + + /** Whether to catch all messages from the connection. */ + private boolean catchAll = false; + + /** + * Creates a new client that operates on the specified connection. + * + * @param connection + * The connection to operate on + */ + public Client(Connection connection) { + this.connection = connection; + connection.addConnectionListener(this); + } + + /** + * Creates a new client that operates on the specified connection and + * immediately executes the specified command. + * + * @param connection + * The connection to operate on + * @param command + * The command to execute + * @throws IOException + * if an I/O error occurs + * @see #execute(Command) + */ + public Client(Connection connection, Command command) throws IOException { + this(connection); + execute(command); + } + + /** + * Returns whether this client catches all messages going over the + * connection. + * + * @return true if the client catches all messages, + * false otherwise + */ + public boolean isCatchAll() { + return catchAll; + } + + /** + * Sets whether this client catches all messages going over the connection. + * + * @param catchAll + * true if the client should catch all messages, + * false otherwise + */ + public void setCatchAll(boolean catchAll) { + this.catchAll = catchAll; + } + + /** + * Executes the specified command. This will also clear the queue of + * messages, discarding all messages that resulted from the previous command + * and have not yet been read. + * + * @param command + * The command to execute + * @throws IOException + * if an I/O error occurs + * @see #execute(Command, boolean) + */ + public void execute(Command command) throws IOException { + execute(command, true); + } + + /** + * Executes the specified command. This will also clear the queue of + * messages, discarding all messages that resulted from the previous + * command and have not yet been read. + * + * @param command + * The command to execute + * @param progressListener + * The progress listener for payload transfers + * @throws IOException + * if an I/O error occurs + * @see #execute(Command, boolean) + */ + public void execute(Command command, ProgressListener progressListener) throws IOException { + execute(command, true, progressListener); + } + + /** + * Executes the specified command and optionally clears the list of + * identifiers this clients listens to before starting the command. + * + * @param command + * The command to execute + * @param removeExistingIdentifiers + * If true, the list of identifiers that this + * clients listens to is cleared + * @throws IOException + * if an I/O error occurs + */ + public void execute(Command command, boolean removeExistingIdentifiers) throws IOException { + execute(command, removeExistingIdentifiers, null); + } + + /** + * Executes the specified command and optionally clears the list of + * identifiers this clients listens to before starting the command. + * + * @param command + * The command to execute + * @param removeExistingIdentifiers + * If 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) { + identifiers.clear(); + } + identifiers.add(command.getIdentifier()); + } + connection.execute(command, progressListener); + } + + /** + * Returns the next message, waiting endlessly for it, if need be. If you + * are not sure whether a message will arrive, better use + * {@link #readMessage(long)} to only wait for a specific time. + * + * @return The next message that resulted from the execution of the last + * command + * @see #readMessage(long) + * @see #execute(Command) + */ + public Message readMessage() { + return readMessage(0); + } + + /** + * Returns the next message. If the message queue is currently empty, at + * least maxWaitTime milliseconds will be waited for a + * message to arrive. + * + * @param maxWaitTime + * The minimum time to wait for a message, in milliseconds + * @return The message, or null if no message arrived in time + * or the client is currently disconnected + * @see #isDisconnected() + * @see Object#wait(long) + */ + public Message readMessage(long maxWaitTime) { + synchronized (messageQueue) { + if (disconnected) { + return null; + } + if (messageQueue.size() == 0) { + try { + messageQueue.wait(maxWaitTime); + } catch (InterruptedException ie1) { + } + } + if (messageQueue.size() > 0) { + return messageQueue.remove(0); + } + } + return null; + } + + /** + * Returns whether the client is currently disconnected. + * + * @return true if the client is disconnected, + * false otherwise + */ + public boolean isDisconnected() { + synchronized (messageQueue) { + return disconnected; + } + } + + /** + * {@inheritDoc} + */ + public void messageReceived(Connection connection, Message message) { + synchronized (messageQueue) { + if (catchAll || (message.getIdentifier().length() == 0) || identifiers.contains(message.getIdentifier())) { + messageQueue.add(message); + messageQueue.notify(); + } + } + } + + /** + * {@inheritDoc} + */ + public void connectionTerminated(Connection connection) { + synchronized (messageQueue) { + disconnected = true; + messageQueue.notify(); + } + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ClientGet.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientGet.java new file mode 100644 index 0000000..5b340e4 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientGet.java @@ -0,0 +1,383 @@ +/* + * jSite - ClientGet.java - Copyright © 2008–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.IOException; +import java.io.Writer; + +/** + * Implementation of the “ClientGet” command. + * + * @author David ‘BombeB Roden <bombe@freenetproject.org> + */ +public class ClientGet extends Command { + + private boolean ignoreDataStore; + private boolean dataStoreOnly; + private String uri; + private Verbosity verbosity = Verbosity.NONE; + private long maxSize = -1; + private long maxTempSize = -1; + private int maxRetries = -1; + private PriorityClass priorityClass = PriorityClass.INTERACTIVE; + private Persistence persistence = Persistence.CONNECTION; + private String clientToken; + private boolean global = false; + private ReturnType returnType = ReturnType.direct; + private boolean binaryBlob = false; + private String allowedMimeTypes = null; + private String filename = null; + private String tempFilename = null; + + /** + *Creates a new ClientGet command with the given request identifier. + * + * @param identifier + * The request identifier + */ + public ClientGet(String identifier) { + super("ClientGet", identifier); + } + + /** + * TODO + * + * @return + */ + public boolean isIgnoreDataStore() { + return ignoreDataStore; + } + + /** + * TODO + * + * @param ignoreDataStore + */ + public void setIgnoreDataStore(boolean ignoreDataStore) { + this.ignoreDataStore = ignoreDataStore; + } + + /** + * TODO + * + * @return + */ + public boolean isDataStoreOnly() { + return dataStoreOnly; + } + + /** + * TODO + * + * @param dataStoreOnly + */ + public void setDataStoreOnly(boolean dataStoreOnly) { + this.dataStoreOnly = dataStoreOnly; + } + + /** + * TODO + * + * @return + */ + public String getUri() { + return uri; + } + + /** + * TODO + * + * @param uri + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * TODO + * + * @return + */ + public Verbosity getVerbosity() { + return verbosity; + } + + /** + * TODO + * + * @param verbosity + */ + public void setVerbosity(Verbosity verbosity) { + this.verbosity = verbosity; + } + + /** + * TODO + * + * @return + */ + public long getMaxSize() { + return maxSize; + } + + /** + * TODO + * + * @param maxSize + */ + public void setMaxSize(long maxSize) { + this.maxSize = maxSize; + } + + /** + * TODO + * + * @return + */ + public long getMaxTempSize() { + return maxTempSize; + } + + /** + * TODO + * + * @param maxTempSize + */ + public void setMaxTempSize(long maxTempSize) { + this.maxTempSize = maxTempSize; + } + + /** + * TODO + * + * @return + */ + public int getMaxRetries() { + return maxRetries; + } + + /** + * TODO + * + * @param maxRetries + */ + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + /** + * TODO + * + * @return + */ + public PriorityClass getPriorityClass() { + return priorityClass; + } + + /** + * TODO + * + * @param priorityClass + */ + public void setPriorityClass(PriorityClass priorityClass) { + this.priorityClass = priorityClass; + } + + /** + * TODO + * + * @return + */ + public Persistence getPersistence() { + return persistence; + } + + /** + * TODO + * + * @param persistence + */ + public void setPersistence(Persistence persistence) { + this.persistence = persistence; + } + + /** + * TODO + * + * @return + */ + public String getClientToken() { + return clientToken; + } + + /** + * TODO + * + * @param clientToken + */ + public void setClientToken(String clientToken) { + this.clientToken = clientToken; + } + + /** + * TODO + * + * @return + */ + public boolean isGlobal() { + return global; + } + + /** + * TODO + * + * @param global + */ + public void setGlobal(boolean global) { + this.global = global; + } + + /** + * TODO + * + * @return + */ + public ReturnType getReturnType() { + return returnType; + } + + /** + * TODO + * + * @param returnType + */ + public void setReturnType(ReturnType returnType) { + this.returnType = returnType; + } + + /** + * TODO + * + * @return + */ + public boolean isBinaryBlob() { + return binaryBlob; + } + + /** + * TODO + * + * @param binaryBlob + */ + public void setBinaryBlob(boolean binaryBlob) { + this.binaryBlob = binaryBlob; + } + + /** + * TODO + * + * @return + */ + public String getAllowedMimeTypes() { + return allowedMimeTypes; + } + + /** + * TODO + * + * @param allowedMimeTypes + */ + public void setAllowedMimeTypes(String allowedMimeTypes) { + this.allowedMimeTypes = allowedMimeTypes; + } + + /** + * TODO + * + * @return + */ + public String getFilename() { + return filename; + } + + /** + * TODO + * + * @param filename + */ + public void setFilename(String filename) { + this.filename = filename; + } + + /** + * TODO + * + * @return + */ + public String getTempFilename() { + return tempFilename; + } + + /** + * TODO + * + * @param tempFilename + */ + public void setTempFilename(String tempFilename) { + this.tempFilename = tempFilename; + } + + /** + * {@inheritDoc} + */ + @Override + protected void write(Writer writer) throws IOException { + super.write(writer); + writer.write("IgnoreDS=" + ignoreDataStore + LINEFEED); + writer.write("DSonly=" + dataStoreOnly + LINEFEED); + writer.write("URI=" + uri + LINEFEED); + writer.write("Verbosity=" + verbosity.getValue() + LINEFEED); + if (maxSize > -1) { + writer.write("MaxSize=" + maxSize + LINEFEED); + } + if (maxTempSize > -1) { + writer.write("MaxTempSize=" + maxTempSize + LINEFEED); + } + if (maxRetries >= -1) { + writer.write("MaxRetries=" + maxRetries + LINEFEED); + } + writer.write("PriorityClass=" + priorityClass.getValue() + LINEFEED); + writer.write("Persistence=" + persistence.getName() + LINEFEED); + if (clientToken != null) { + writer.write("ClientToken=" + clientToken + LINEFEED); + } + writer.write("Global=" + global + LINEFEED); + writer.write("BinaryBlob=" + binaryBlob + LINEFEED); + if (allowedMimeTypes != null) { + writer.write("AllowedMIMETypes=" + allowedMimeTypes + LINEFEED); + } + if (returnType == ReturnType.disk) { + writer.write("Filename=" + filename + LINEFEED); + if (tempFilename != null) { + writer.write("TempFilename=" + tempFilename + LINEFEED); + } + } + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ClientHello.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientHello.java new file mode 100644 index 0000000..5983078 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientHello.java @@ -0,0 +1,100 @@ +/* + * jSite - ClientHello.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.IOException; +import java.io.Writer; + +/** + * Implementation of the ClientHello command. This command must + * be sent as the first command on a connection ({@link de.todesbaum.util.freenet.fcp2.Connection#connect()} + * takes care of that) and must not be sent afterwards. + *

+ * The node can answer with the following messages: NodeHello. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class ClientHello extends Command { + + /** The name of the client. */ + protected String name; + + /** The version of the FCP protocol the client expects. */ + protected String expectedVersion = "2.0"; + + /** + * Creates a new ClientHello command. + */ + public ClientHello() { + super("ClientHello", "ClientHello-" + System.currentTimeMillis()); + } + + /** + * Returns the value of the ExpectedVersion parameter of this + * command. At the moment this value is not used by the node but in the + * future this may be used to enforce certain node versions. + * + * @return The expected version + */ + public String getExpectedVersion() { + return expectedVersion; + } + + /** + * Sets the value of the ExpectedVersion parameter of this + * command. At the moment this value is not used by the node but in the + * future this may be used to enforce certain node versions. + * + * @param expectedVersion + * The expected version + */ + public void setExpectedVersion(String expectedVersion) { + this.expectedVersion = expectedVersion; + } + + /** + * Returns the name of the client that is connecting. + * + * @return The name of the client + */ + public String getName() { + return name; + } + + /** + * Sets the name of the client that is connecting. + * + * @param name + * The name of the client + */ + public void setName(String name) { + this.name = name; + } + + /** + * {@inheritDoc} + */ + @Override + protected void write(Writer writer) throws IOException { + writer.write("Name=" + name + LINEFEED); + writer.write("ExpectedVersion=" + expectedVersion + LINEFEED); + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPut.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPut.java new file mode 100644 index 0000000..9993173 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPut.java @@ -0,0 +1,243 @@ +/* + * jSite - ClientPut.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.IOException; +import java.io.Writer; + +/** + * Abstract base class for all put requests. It contains all parameters that put + * requests have in common. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public abstract class ClientPut extends Command { + + /** The URI of this request. */ + protected final String uri; + + /** The client token of this request. */ + protected String clientToken = null; + + /** Whether this request should only create a CHK. */ + protected boolean getCHKOnly = false; + + /** Whether this request is a global request. */ + protected boolean global = false; + + /** Whether the node should not try to compress the file. */ + protected boolean dontCompress = false; + + /** The maximum number of retries of this command. */ + protected int maxRetries = 0; + + /** Whether to generate the keys early. */ + protected boolean earlyEncode = false; + + /** The persistence of this request. */ + protected Persistence persistence = Persistence.CONNECTION; + + /** The priority class of this request. */ + protected PriorityClass priorityClass = PriorityClass.INTERACTIVE; + + /** The verbosiry of this request. */ + protected Verbosity verbosity = Verbosity.NONE; + + /** + * Creates a new put request with the specified name, identifier and URI. + * + * @param name + * The name of this request + * @param identifier + * The identifier of this request + * @param uri + * The URI of this request + */ + protected ClientPut(String name, String identifier, String uri) { + super(name, identifier); + this.uri = uri; + } + + /** + * Returns whether the node should not try to compress the data. + * + * @return true if the node should not try + * to compress the data + */ + public boolean isDontCompress() { + return dontCompress; + } + + /** + * Sets whether the node should not try to compress the data. A client might + * set this hint on data that is clearly not compressible, like MPEG audio + * files, JPEG or PNG images, highly compressed movies, or compressed + * archives like ZIP files. Otherwise the node will try to compress the file + * which -- depending on the size of the data -- might take a lot of time + * and memory. + * + * @param dontCompress + * true if the node should not + * try to compress the data + */ + public void setDontCompress(boolean dontCompress) { + this.dontCompress = dontCompress; + } + + /** + * Returns whether this request should only return the CHK of the data. + * @return Whether this request should only return the CHK of the data + */ + public boolean isGetCHKOnly() { + return getCHKOnly; + } + + /** + * Sets whether this request should only return the CHK of the data. + * @param getCHKOnly + * true if this request should only return the CHK of the data + */ + public void setGetCHKOnly(boolean getCHKOnly) { + this.getCHKOnly = getCHKOnly; + } + + /** + * Returns whether this request is a global request. + * @return true if this request is a global request, false otherwise + */ + public boolean isGlobal() { + return global; + } + + /** + * Sets whether this request is a global request. + * @param global + * true if this request is a global request, false otherwise + */ + public void setGlobal(boolean global) { + this.global = global; + } + + /** + * Returns the maximum number of retries of this request. + * @return The maximum number of retries of this request + */ + public int getMaxRetries() { + return maxRetries; + } + + /** + * Sets the maximum number of retries of this request + * @param maxRetries + * The maximum number of retries of this request + */ + public void setMaxRetries(int maxRetries) { + this.maxRetries = maxRetries; + } + + /** + * Returns whether the data should be encoded early to generate the final + * key as fast as possible. + * + * @return {@code true} if the key should be generated early, {@code false} + * otherwise + */ + public boolean isEarlyEncode() { + return earlyEncode; + } + + /** + * Sets whether the data should be encoded early to generate the final key + * as fast as possible. + * + * @param earlyEncode + * {@code true} if the key should be generated early, {@code + * false} otherwise + */ + public void setEarlyEncode(boolean earlyEncode) { + this.earlyEncode = earlyEncode; + } + + /** + * Returns the priority class of this request. + * @return The priority class of this request + */ + public PriorityClass getPriorityClass() { + return priorityClass; + } + + /** + * Sets the priority class of this request. + * @param priorityClass + * The priority class of this request + */ + public void setPriorityClass(PriorityClass priorityClass) { + this.priorityClass = priorityClass; + } + + /** + * Returns the verbosity of this request. + * @return The verbosity of this request + */ + public Verbosity getVerbosity() { + return verbosity; + } + + /** + * Sets the verbosity of this request. + * @param verbosity + * The verbosity of this request + */ + public void setVerbosity(Verbosity verbosity) { + this.verbosity = verbosity; + } + + /** + * Returns the URI of this request + * @return The URI of this request. + */ + public String getUri() { + return uri; + } + + /** + * {@inheritDoc} + */ + @Override + protected void write(Writer writer) throws IOException { + super.write(writer); + writer.write("URI=" + uri + LINEFEED); + if (verbosity != null) + writer.write("Verbosity=" + verbosity.getValue() + LINEFEED); + if (maxRetries != 0) + writer.write("MaxRetries=" + maxRetries + LINEFEED); + writer.write("EarlyEncode=" + earlyEncode); + if (priorityClass != null) + writer.write("PriorityClass=" + priorityClass.getValue() + LINEFEED); + writer.write("GetCHKOnly=" + getCHKOnly + LINEFEED); + writer.write("Global=" + global + LINEFEED); + writer.write("DontCompress=" + dontCompress + LINEFEED); + if (clientToken != null) + writer.write("ClientToken=" + clientToken + LINEFEED); + if (persistence != null) + writer.write("Persistence=" + persistence.getName() + LINEFEED); + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java new file mode 100644 index 0000000..d72f2c1 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutComplexDir.java @@ -0,0 +1,187 @@ +/* + * jSite - ClientPutComplexDir.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; + +import de.todesbaum.util.io.Closer; + +/** + * Implementation of the ClientPutComplexDir command. This command + * can be used to insert directories that do not exist on disk. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class ClientPutComplexDir extends ClientPutDir { + + /** The file entries of this directory. */ + private List fileEntries = new ArrayList(); + + /** Whether this request has payload. */ + private boolean hasPayload = false; + + /** The input streams for the payload. */ + private File payloadFile; + + /** The total number of bytes of the payload. */ + private long payloadLength = 0; + + /** The temp directory to use. */ + private final String tempDirectory; + + /** + * Creates a new ClientPutComplexDir command with the specified + * identifier and URI. + * + * @param identifier + * The identifier of the command + * @param uri + * The URI of the command + */ + public ClientPutComplexDir(String identifier, String uri) { + this(identifier, uri, null); + } + + /** + * Creates a new ClientPutComplexDir command with the specified + * identifier and URI. + * + * @param identifier + * The identifier of the command + * @param uri + * The URI of the command + * @param tempDirectory + * The temp directory to use, or {@code null} to use the default + * temp directory + */ + public ClientPutComplexDir(String identifier, String uri, String tempDirectory) { + super("ClientPutComplexDir", identifier, uri); + this.tempDirectory = tempDirectory; + } + + /** + * Adds a file to the directory inserted by this request. + * + * @param fileEntry + * The file entry to add to the directory + * @throws IOException + * if an I/O error occurs when creating the payload stream + */ + public void addFileEntry(FileEntry fileEntry) throws IOException { + if (fileEntry instanceof DirectFileEntry) { + if (payloadFile == null) { + try { + payloadFile = File.createTempFile("payload", ".dat", (tempDirectory != null) ? new File(tempDirectory) : null); + payloadFile.deleteOnExit(); + } catch (IOException e) { + /* ignore. */ + } + } + if (payloadFile != null) { + InputStream payloadInputStream = ((DirectFileEntry) fileEntry).getDataInputStream(); + FileOutputStream payloadOutputStream = null; + try { + payloadOutputStream = new FileOutputStream(payloadFile, true); + byte[] buffer = new byte[65536]; + int read = 0; + while ((read = payloadInputStream.read(buffer)) != -1) { + payloadOutputStream.write(buffer, 0, read); + } + payloadOutputStream.flush(); + fileEntries.add(fileEntry); + } catch (IOException ioe1) { + payloadFile.delete(); + throw ioe1; + } finally { + Closer.close(payloadOutputStream); + Closer.close(payloadInputStream); + } + } + } else { + fileEntries.add(fileEntry); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void write(Writer writer) throws IOException { + super.write(writer); + int fileIndex = 0; + for (FileEntry fileEntry : fileEntries) { + writer.write("Files." + fileIndex + ".Name=" + fileEntry.getFilename() + LINEFEED); + if (fileEntry.getContentType() != null) { + writer.write("Files." + fileIndex + ".Metadata.ContentType=" + fileEntry.getContentType() + LINEFEED); + } + writer.write("Files." + fileIndex + ".UploadFrom=" + fileEntry.getName() + LINEFEED); + if (fileEntry instanceof DirectFileEntry) { + hasPayload = true; + writer.write("Files." + fileIndex + ".DataLength=" + ((DirectFileEntry) fileEntry).getDataLength() + LINEFEED); + payloadLength += ((DirectFileEntry) fileEntry).getDataLength(); + } else if (fileEntry instanceof DiskFileEntry) { + writer.write("Files." + fileIndex + ".Filename=" + ((DiskFileEntry) fileEntry).getFilename() + LINEFEED); + } else if (fileEntry instanceof RedirectFileEntry) { + writer.write("Files." + fileIndex + ".TargetURI=" + ((RedirectFileEntry) fileEntry).getTargetURI() + LINEFEED); + } + fileIndex++; + } + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean hasPayload() { + return hasPayload; + } + + /** + * {@inheritDoc} + */ + @Override + protected long getPayloadLength() { + return payloadLength; + } + + /** + * {@inheritDoc} + */ + @Override + protected InputStream getPayload() { + if (payloadFile != null) { + try { + return new FileInputStream(payloadFile); + } catch (FileNotFoundException e) { + /* shouldn't occur. */ + } + } + return null; + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutDir.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutDir.java new file mode 100644 index 0000000..386fe1d --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/ClientPutDir.java @@ -0,0 +1,167 @@ +/* + * jSite - ClientPutDir.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.IOException; +import java.io.Writer; + +/** + * Abstract base class for all put requests that insert a directory. + * + * @param + * The type of the “ClientPutDir” command + * @author David Roden <droden@gmail.com> + */ +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. + * + * @param name + * The name of the request + * @param identifier + * The identifier of the request + * @param uri + * The URI of the request + */ + public ClientPutDir(String name, String identifier, String uri) { + super(name, identifier, uri); + } + + /** + * Returns the default name of the directory. + * + * @return The default name of the directory + */ + public String getDefaultName() { + return defaultName; + } + + /** + * Sets the default name of the directory. The default name of a directory + * is the name of the file that will be delivered if the directory was + * requested without a filename. It's about the same as the + * index.html file that gets delivered if you only request a + * directory from a webserver. + * + * @param defaultName + * The default name of the directory + */ + public void setDefaultName(String defaultName) { + this.defaultName = defaultName; + } + + /** + * Returns the current manifest putter. + * + * @return The current manifest putter (may be {@code null}) + */ + public ManifestPutter getManifestPutter() { + return manifestPutter; + } + + /** + * Sets the manifest putter for the “ClientPutDir” command. If {@code null} + * is given the node will choose a manifest putter. + * + * @param manifestPutter + * The manifest putter to use for the command (may be + * {@code null}) + * @return This ClientPutDir command + */ + @SuppressWarnings("unchecked") + public C setManifestPutter(ManifestPutter manifestPutter) { + this.manifestPutter = manifestPutter; + return (C) this; + } + + /** + * {@inheritDoc} + */ + @Override + protected void write(Writer writer) throws IOException { + super.write(writer); + if (defaultName != null) + writer.write("DefaultName=" + defaultName + LINEFEED); + if (manifestPutter != null) { + writer.write("ManifestPutter=" + manifestPutter.getName() + LINEFEED); + } + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Command.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Command.java new file mode 100644 index 0000000..63aaa9c --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/Command.java @@ -0,0 +1,137 @@ +/* + * jSite - Command.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; + +/** + * Abstract base class for all commands. + *

+ * In addition to the replies listed at the type comment of each specific + * command the node can always send the following messages: + * ProtocolError (if this library screws up), + * CloseConnectionDuplicateClientName (if a client with the same + * name of the {@link de.todesbaum.util.freenet.fcp2.Connection} connects). So + * when receiving messages from the node you should always be prepared for + * something you did not expect. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public abstract class Command { + + /** The line feed sequence used by the library. */ + protected static final String LINEFEED = "\r\n"; + + /** + * The name of the command. The name is sent to the node so it can not be + * chosen arbitrarily! + */ + private final String commandName; + + /** + * The identifier of the command. This identifier is used to identify + * replies that are caused by a command. + */ + private final String identifier; + + /** + * Creates a new command with the specified name and identifier. + * + * @param name + * The name of the command + * @param identifier + * The identifier of the command + */ + public Command(String name, String identifier) { + this.commandName = name; + this.identifier = identifier; + } + + /** + * Returns the name of this command. + * + * @return The name of this command + */ + public String getCommandName() { + return commandName; + } + + /** + * Return the identifier of this command. + * + * @return The identifier of this command + */ + public String getIdentifier() { + return identifier; + } + + /** + * Writes all parameters to the specified writer. + *

+ * NOTE: Subclasses of Command must call + * super.write(writer) before or after writing their own + * parameters! + * + * @param writer + * The stream to write the parameters to + * @throws IOException + * if an I/O error occurs + */ + protected void write(Writer writer) throws IOException { + if (identifier != null) + writer.write("Identifier=" + identifier + LINEFEED); + } + + /** + * Returns whether this command has payload to send after the message. + * Subclasses need to return true here if they need to send + * payload after the message. + * + * @return true if this command has payload to send, + * false otherwise + */ + protected boolean hasPayload() { + return false; + } + + /** + * Returns the payload of this command as an {@link InputStream}. This + * method is never called if {@link #hasPayload()} returns + * false. + * + * @return The payload of this command + */ + protected InputStream getPayload() { + return null; + } + + /** + * Returns the length of the payload. This method is never called if + * {@link #hasPayload()} returns false. + * + * @return The length of the payload + */ + protected long getPayloadLength() { + return -1; + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Connection.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Connection.java new file mode 100644 index 0000000..c4cb669 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/Connection.java @@ -0,0 +1,390 @@ +/* + * jSite - Connection.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.net.Socket; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +import de.todesbaum.util.io.Closer; +import de.todesbaum.util.io.LineInputStream; +import de.todesbaum.util.io.StreamCopier; +import de.todesbaum.util.io.StreamCopier.ProgressListener; +import de.todesbaum.util.io.TempFileInputStream; + +/** + * A physical connection to a Freenet node. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class Connection { + + /** The listeners that receive events from this connection. */ + private List connectionListeners = new ArrayList(); + + /** The node this connection is connected to. */ + private final Node node; + + /** The name of this connection. */ + private final String name; + + /** The network socket of this connection. */ + private Socket nodeSocket; + + /** The input stream that reads from the socket. */ + private InputStream nodeInputStream; + + /** The output stream that writes to the socket. */ + private OutputStream nodeOutputStream; + + /** The thread that reads from the socket. */ + private NodeReader nodeReader; + + /** A writer for the output stream. */ + private Writer nodeWriter; + + /** The NodeHello message sent by the node on connect. */ + protected Message nodeHello; + + /** The temp directory to use. */ + private String tempDirectory; + + /** + * Creates a new connection to the specified node with the specified name. + * + * @param node + * The node to connect to + * @param name + * The name of this connection + */ + public Connection(Node node, String name) { + this.node = node; + this.name = name; + } + + /** + * Adds a listener that gets notified on connection events. + * + * @param connectionListener + * The listener to add + */ + public void addConnectionListener(ConnectionListener connectionListener) { + connectionListeners.add(connectionListener); + } + + /** + * Removes a listener from the list of registered listeners. Only the first + * matching listener is removed. + * + * @param connectionListener + * The listener to remove + * @see List#remove(java.lang.Object) + */ + public void removeConnectionListener(ConnectionListener connectionListener) { + connectionListeners.remove(connectionListener); + } + + /** + * Notifies listeners about a received message. + * + * @param message + * The received message + */ + protected void fireMessageReceived(Message message) { + for (ConnectionListener connectionListener : connectionListeners) { + connectionListener.messageReceived(this, message); + } + } + + /** + * Notifies listeners about the loss of the connection. + */ + protected void fireConnectionTerminated() { + for (ConnectionListener connectionListener : connectionListeners) { + connectionListener.connectionTerminated(this); + } + } + + /** + * Returns the name of the connection. + * + * @return The name of the connection + */ + public String getName() { + return name; + } + + /** + * Sets the temp directory to use for creation of temporary files. + * + * @param tempDirectory + * The temp directory to use, or {@code null} to use the default + * temp directory + */ + public void setTempDirectory(String tempDirectory) { + this.tempDirectory = tempDirectory; + } + + /** + * Connects to the node. + * + * @return true if the connection succeeded and the node + * returned a NodeHello message + * @throws IOException + * if an I/O error occurs + * @see #getNodeHello() + */ + public synchronized boolean connect() throws IOException { + nodeSocket = null; + nodeInputStream = null; + nodeOutputStream = null; + nodeWriter = null; + nodeReader = null; + try { + nodeSocket = new Socket(node.getHostname(), node.getPort()); + nodeSocket.setReceiveBufferSize(65535); + nodeInputStream = nodeSocket.getInputStream(); + nodeOutputStream = nodeSocket.getOutputStream(); + nodeWriter = new OutputStreamWriter(nodeOutputStream, Charset.forName("UTF-8")); + nodeReader = new NodeReader(nodeInputStream); + Thread nodeReaderThread = new Thread(nodeReader); + nodeReaderThread.setDaemon(true); + nodeReaderThread.start(); + ClientHello clientHello = new ClientHello(); + clientHello.setName(name); + clientHello.setExpectedVersion("2.0"); + execute(clientHello); + synchronized (this) { + try { + wait(); + } catch (InterruptedException e) { + } + } + return nodeHello != null; + } catch (IOException ioe1) { + disconnect(); + throw ioe1; + } + } + + /** + * Returns whether this connection is still connected to the node. + * + * @return true if this connection is still valid, + * false otherwise + */ + public boolean isConnected() { + return (nodeHello != null) && (nodeSocket != null) && (nodeSocket.isConnected()); + } + + /** + * Returns the NodeHello message the node sent on connection. + * + * @return The NodeHello message of the node + */ + public Message getNodeHello() { + return nodeHello; + } + + /** + * Disconnects from the node. + */ + public void disconnect() { + Closer.close(nodeWriter); + nodeWriter = null; + Closer.close(nodeOutputStream); + nodeOutputStream = null; + Closer.close(nodeInputStream); + nodeInputStream = null; + nodeInputStream = null; + Closer.close(nodeSocket); + nodeSocket = null; + synchronized (this) { + notify(); + } + fireConnectionTerminated(); + } + + /** + * Executes the specified command. + * + * @param command + * The command to execute + * @throws IllegalStateException + * if the connection is not connected + * @throws IOException + * if an I/O error occurs + */ + public synchronized void execute(Command command) throws IllegalStateException, IOException { + execute(command, null); + } + + /** + * Executes the specified command. + * + * @param command + * The command to execute + * @param progressListener + * A progress listener for a payload transfer + * @throws IllegalStateException + * if the connection is not connected + * @throws IOException + * if an I/O error occurs + */ + public synchronized void execute(Command command, ProgressListener progressListener) throws IllegalStateException, IOException { + if (nodeSocket == null) { + throw new IllegalStateException("connection is not connected"); + } + nodeWriter.write(command.getCommandName() + Command.LINEFEED); + command.write(nodeWriter); + nodeWriter.write("EndMessage" + Command.LINEFEED); + nodeWriter.flush(); + if (command.hasPayload()) { + InputStream payloadInputStream = null; + try { + payloadInputStream = command.getPayload(); + StreamCopier.copy(payloadInputStream, nodeOutputStream, command.getPayloadLength(), progressListener); + } finally { + Closer.close(payloadInputStream); + } + nodeOutputStream.flush(); + } + } + + /** + * The reader thread for this connection. This is essentially a thread that + * reads lines from the node, creates messages from them and notifies + * listeners about the messages. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ + private class NodeReader implements Runnable { + + /** The input stream to read from. */ + @SuppressWarnings("hiding") + private InputStream nodeInputStream; + + /** + * Creates a new reader that reads from the specified input stream. + * + * @param nodeInputStream + * The input stream to read from + */ + public NodeReader(InputStream nodeInputStream) { + this.nodeInputStream = nodeInputStream; + } + + /** + * Main loop of the reader. Lines are read and converted into + * {@link Message} objects. + */ + @SuppressWarnings("synthetic-access") + public void run() { + LineInputStream nodeReader = null; + try { + nodeReader = new LineInputStream(nodeInputStream); + String line = ""; + Message message = null; + while (line != null) { + line = nodeReader.readLine(); + // System.err.println("> " + line); + if (line == null) { + break; + } + if (message == null) { + message = new Message(line); + continue; + } + if ("Data".equals(line)) { + /* need to read message from stream now */ + File tempFile = null; + try { + tempFile = File.createTempFile("fcpv2", "data", (tempDirectory != null) ? new File(tempDirectory) : null); + tempFile.deleteOnExit(); + FileOutputStream tempFileOutputStream = new FileOutputStream(tempFile); + long dataLength = Long.parseLong(message.get("DataLength")); + StreamCopier.copy(nodeInputStream, tempFileOutputStream, dataLength); + tempFileOutputStream.close(); + message.setPayloadInputStream(new TempFileInputStream(tempFile)); + } catch (IOException ioe1) { + ioe1.printStackTrace(); + } + } + if ("Data".equals(line) || "EndMessage".equals(line)) { + if (message.getName().equals("NodeHello")) { + nodeHello = message; + synchronized (Connection.this) { + Connection.this.notify(); + } + } else { + fireMessageReceived(message); + } + message = null; + continue; + } + int equalsPosition = line.indexOf('='); + if (equalsPosition > -1) { + String key = line.substring(0, equalsPosition).trim(); + String value = line.substring(equalsPosition + 1).trim(); + if (key.equals("Identifier")) { + message.setIdentifier(value); + } else { + message.put(key, value); + } + continue; + } + /* skip lines consisting of whitespace only */ + if (line.trim().length() == 0) { + continue; + } + /* if we got here, some error occured! */ + throw new IOException("Unexpected line: " + line); + } + } catch (IOException ioe1) { + // ioe1.printStackTrace(); + } finally { + if (nodeReader != null) { + try { + nodeReader.close(); + } catch (IOException ioe1) { + } + } + if (nodeInputStream != null) { + try { + nodeInputStream.close(); + } catch (IOException ioe1) { + } + } + } + Connection.this.disconnect(); + } + + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ConnectionListener.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ConnectionListener.java new file mode 100644 index 0000000..8e55a2d --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/ConnectionListener.java @@ -0,0 +1,49 @@ +/* + * jSite - ConnectionListener.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.util.EventListener; + +/** + * Interface for clients that want to be notified when a message was received. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public interface ConnectionListener extends EventListener { + + /** + * Notifies a client that a message was received. + * + * @param connection + * The connection the message was received on + * @param message + * The message that was received + */ + public void messageReceived(Connection connection, Message message); + + /** + * Notifies a client that the connection to the node has been lost. + * + * @param connection + * The connection that was lost + */ + public void connectionTerminated(Connection connection); + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java new file mode 100644 index 0000000..9b12e74 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/DirectFileEntry.java @@ -0,0 +1,99 @@ +/* + * jSite - DirectFileEntry.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * A {@link FileEntry} that sends its payload directly to the node, using the + * existing FCP connection. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class DirectFileEntry extends FileEntry { + + /** The input stream to read the data for this file from. */ + private final InputStream dataInputStream; + + /** The length of the data. */ + private final long dataLength; + + /** + * Creates a new FileEntry with the specified name and content type that + * gets its data from the specified byte array. + * + * @param filename + * The name of the file + * @param contentType + * The content type of the file + * @param dataBytes + * The content of the file + */ + public DirectFileEntry(String filename, String contentType, byte[] dataBytes) { + this(filename, contentType, new ByteArrayInputStream(dataBytes), dataBytes.length); + } + + /** + * Creates a new FileEntry with the specified name and content type that + * gets its data from the specified input stream. + * + * @param filename + * The name of the file + * @param contentType + * The content type of the file + * @param dataInputStream + * The input stream to read the content from + * @param dataLength + * The length of the data input stream + */ + public DirectFileEntry(String filename, String contentType, InputStream dataInputStream, long dataLength) { + super(filename, contentType); + this.dataInputStream = dataInputStream; + this.dataLength = dataLength; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return "direct"; + } + + /** + * Returns the input stream for the file's content. + * + * @return The input stream for the file's content + */ + public InputStream getDataInputStream() { + return dataInputStream; + } + + /** + * Returns the length of this file's content. + * + * @return The length of this file's content + */ + public long getDataLength() { + return dataLength; + } + +} \ No newline at end of file diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java new file mode 100644 index 0000000..bf036b4 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/DiskFileEntry.java @@ -0,0 +1,66 @@ +/* + * jSite - DiskFileEntry.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * A {@link FileEntry} that reads the content from a file on the disk. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class DiskFileEntry extends FileEntry { + + /** The local file name. */ + private final String localFilename; + + /** + * Creates a new {@link FileEntry} with the specified name and content type + * that is read from the file specified by localFilename. + * + * @param filename + * The name of the file + * @param contentType + * The content type of the file + * @param localFilename + * The name of the local file that holds the content of the file + * to insert + */ + public DiskFileEntry(String filename, String contentType, String localFilename) { + super(filename, contentType); + this.localFilename = localFilename; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return "disk"; + } + + /** + * Returns the name of the local file that holds the content for this file. + * + * @return The name of the local file + */ + public String getLocalFilename() { + return localFilename; + } + +} \ No newline at end of file diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/FileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/FileEntry.java new file mode 100644 index 0000000..7adb11f --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/FileEntry.java @@ -0,0 +1,82 @@ +/* + * jSite - FileEntry.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * Abstract base class of file entries that are used in the + * {@link de.todesbaum.util.freenet.fcp2.ClientPutComplexDir} command to define + * the files of an insert. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public abstract class FileEntry { + + /** The name of the file. */ + private final String filename; + + /** The content type of the file. */ + private final String contentType; + + /** + * Creates a new file entry with the specified name and content type. The + * content type should be a standard MIME type with an additional charset + * specification for text-based types. + * + * @param filename + * The name of the file + * @param contentType + * The content type of the file, e.g. + * "application/x-tar" or + * "text/html; charset=iso8859-15" + */ + protected FileEntry(String filename, String contentType) { + this.filename = filename; + this.contentType = contentType; + } + + /** + * Returns the name of this entry's type. Can be one of direct, + * disk, or redirect. This method is + * implemented by the subclasses {@link DirectFileEntry}, + * {@link DiskFileEntry}, and {@link RedirectFileEntry}, respectively. + * + * @return The name of this entry's type + */ + public abstract String getName(); + + /** + * Returns the content type of this file. + * + * @return The content type of this file + */ + public String getContentType() { + return contentType; + } + + /** + * Returns the name of this file. + * + * @return The name of this file + */ + public String getFilename() { + return filename; + } + +} \ No newline at end of file diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/GenerateSSK.java b/src/main/java/de/todesbaum/util/freenet/fcp2/GenerateSSK.java new file mode 100644 index 0000000..84e4bf6 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/GenerateSSK.java @@ -0,0 +1,38 @@ +/* + * jSite - GenerateSSK.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * Implementation of the GenerateSSK command. + *

+ * The node can answer with the following messages: SSKKeypair. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class GenerateSSK extends Command { + + /** + * Creates a new GenerateSSK request. + */ + public GenerateSSK() { + super("GenerateSSK", null); + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Message.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Message.java new file mode 100644 index 0000000..6c934ec --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/Message.java @@ -0,0 +1,175 @@ +/* + * jSite - Message.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Contains replies sent by the Freenet node. A message always has a name, and + * most of the messages also have an identifier which binds it to a specific + * command. Exceptions are among others NodeHello, + * SSKKeypair, and EndListPersistentRequests. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + * @see de.todesbaum.util.freenet.fcp2.Client + */ +public class Message { + + /** The name of this message. */ + private final String name; + + /** The identifier of this message. */ + private String identifier = ""; + + /** The parameters of this message. */ + private Map parameters = new HashMap(); + + /** The payload. */ + private InputStream payloadInputStream; + + /** + * Creates a new message with the specified name. + * + * @param name + * The name of this message + */ + public Message(String name) { + this.name = name; + } + + /** + * Returns the identifier of this message. + * + * @return The identifier + */ + public String getIdentifier() { + return identifier; + } + + /** + * Sets the identifier of this message. + * + * @param identifier + * The identifier of this message + */ + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + /** + * Returns the name of this message. + * + * @return The name of this message + */ + public String getName() { + return name; + } + + /** + * Tests whether this message contains the parameter with the specified key. + * Key names are compared ignoring case. + * + * @param key + * The name of the parameter + * @return true if this parameter exists in this message, + * false otherwise + */ + public boolean containsKey(String key) { + return parameters.containsKey(key.toLowerCase()); + } + + /** + * Returns all parameters of this message. The keys of the entries are all + * lower case so if you want to match the parameter names you have to watch + * out. + * + * @return All parameters of this message + */ + public Set> entrySet() { + return parameters.entrySet(); + } + + /** + * Returns the value of the parameter with the name specified by + * key. + * + * @param key + * The name of the parameter + * @return The value of the parameter + */ + public String get(String key) { + return parameters.get(key.toLowerCase()); + } + + /** + * Stores the specified value as parameter with the name specified by + * key. + * + * @param key + * The name of the parameter + * @param value + * The value of the parameter + * @return The previous value, or null if there was no + * previous value + */ + public String put(String key, String value) { + return parameters.put(key.toLowerCase(), value); + } + + /** + * Returns the number of parameters in this message. + * + * @return The number of parameters + */ + public int size() { + return parameters.size(); + } + + /** + * @return Returns the payloadInputStream. + */ + public InputStream getPayloadInputStream() { + return payloadInputStream; + } + + /** + * @param payloadInputStream + * The payloadInputStream to set. + */ + public void setPayloadInputStream(InputStream payloadInputStream) { + this.payloadInputStream = payloadInputStream; + } + + /** + * Returns a textual representation of this message, containing its name, + * the identifier, and the parameters. + * + * @return A textual representation of this message + */ + @Override + public String toString() { + return name + "[identifier=" + identifier + ",parameters=" + parameters.toString() + "]"; + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Node.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Node.java new file mode 100644 index 0000000..d2fa91c --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/Node.java @@ -0,0 +1,81 @@ +/* + * jSite - Node.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * Contains the hostname and port number of the Freenet node. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class Node { + + /** The default port of FCPv2. */ + public static final int DEFAULT_PORT = 9481; + + /** The hostname of the node. */ + protected String hostname; + + /** The port number of the node. */ + protected int port; + + /** + * Creates a new node with the specified hostname and the default port + * number. + * + * @param hostname + * The hostname of the node + * @see #DEFAULT_PORT + */ + public Node(String hostname) { + this(hostname, DEFAULT_PORT); + } + + /** + * Creates a new node with the specified hostname and port number. + * + * @param hostname + * The hostname of the node + * @param port + * The port number of the node + */ + public Node(String hostname, int port) { + this.hostname = hostname; + this.port = port; + } + + /** + * Returns the hostname of the node. + * + * @return The hostname of the node + */ + public String getHostname() { + return hostname; + } + + /** + * Returns the port number of the node. + * + * @return The port number of the node + */ + public int getPort() { + return port; + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Persistence.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Persistence.java new file mode 100644 index 0000000..55e73f8 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/Persistence.java @@ -0,0 +1,87 @@ +/* + * jSite - Persistence.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * The possible persistence options. This specify whether (and for how long) the + * node remembers to execute a request and the results. Possible values are + * connection, reboot, and forever. + * connection means that a request is aborted as soon as the + * connection to the node is severed. reboot means that a request + * is remembered as long as the node is running but not after restarts. + * forever finally means that a request persists until it is + * explicitely deleted. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + * @see de.todesbaum.util.freenet.fcp2.ModifyPersistentRequest + * @see de.todesbaum.util.freenet.fcp2.RemovePersistentRequest + */ +public final class Persistence { + + /** + * Denotes that a request should be terminated if the connection to the node + * is severed. + */ + public static final Persistence CONNECTION = new Persistence("connection"); + + /** Denotes that a request should be remembered until the node is restarted. */ + public static final Persistence REBOOT = new Persistence("reboot"); + + /** + * Denotes that a request should be remembered until it is explicitely + * deleted. + */ + public static final Persistence FOREVER = new Persistence("forever"); + + /** The name of this persistence option. */ + private String name; + + /** + * Private constructor that creates a persistence option with the specified + * name. + * + * @param name + * The name of the persistence option. + */ + private Persistence(String name) { + this.name = name; + } + + /** + * Returns the name of this persistence option. + * + * @return The name of this persistence option + */ + public String getName() { + return name; + } + + /** + * Returns a textual representation of this persistence option. The result + * is identical to calling {@link #getName()}. + * + * @return The name of this persistence option + */ + @Override + public String toString() { + return name; + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/PriorityClass.java b/src/main/java/de/todesbaum/util/freenet/fcp2/PriorityClass.java new file mode 100644 index 0000000..623bc5b --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/PriorityClass.java @@ -0,0 +1,125 @@ +/* + * jSite - PriorityClass.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * The possible priority classes. Possible values are, in order of descending + * priority: maximum (anything more important than fproxy), + * interactive (fproxy), semi-interactive (fproxy + * immediate mode large file downloads, not to disk), updatable + * (updatable site checks), bulk (large file downloads to disk), + * prefetch, minimum. + * + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public final class PriorityClass { + + /** Denotes maximum priority class. */ + public static final PriorityClass MAXIMUM = new PriorityClass("maximum", 0); + + /** Denotes interactive priority class. */ + public static final PriorityClass INTERACTIVE = new PriorityClass("interactive", 1); + + /** Denotes semi-interactive priority class. */ + public static final PriorityClass SEMI_INTERACTIVE = new PriorityClass("semiInteractive", 2); + + /** Denotes updatable priority class. */ + public static final PriorityClass UPDATABLE = new PriorityClass("updatable", 3); + + /** Denotes bulk priority class. */ + public static final PriorityClass BULK = new PriorityClass("bulk", 4); + + /** Denotes prefetch priority class. */ + public static final PriorityClass PREFETCH = new PriorityClass("prefetch", 5); + + /** Denotes minimum priority class. */ + public static final PriorityClass MINIMUM = new PriorityClass("minimum", 6); + + /** The name of the priority class. */ + private String name; + + /** The value of the priority class. */ + private int value; + + /** + * Creates a new priority class with the specified name and value. + * + * @param name + * The name of the priority class + * @param value + * The value of the priority class + */ + private PriorityClass(String name, int value) { + this.name = name; + this.value = value; + } + + /** + * Returns the name of this priority class. + * + * @return The name of this priority class + */ + public String getName() { + return name; + } + + /** + * Returns the value of this priority class. + * + * @return The value of this priority class + */ + public int getValue() { + return value; + } + + // + // STATIC METHODS + // + + /** + * Returns the priority class with the given name, matched case-insensitive. + * + * @param value + * The name of the priority + * @return The priority with the given name, or {@code null} if no priority + * matches the given name + */ + public static PriorityClass valueOf(String value) { + for (PriorityClass priorityClass : new PriorityClass[] { MINIMUM, PREFETCH, BULK, UPDATABLE, SEMI_INTERACTIVE, INTERACTIVE, MAXIMUM }) { + if (priorityClass.getName().equalsIgnoreCase(value)) { + return priorityClass; + } + } + return null; + } + + // + // OBJECT METHODS + // + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return name; + } + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java b/src/main/java/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java new file mode 100644 index 0000000..06a2171 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/RedirectFileEntry.java @@ -0,0 +1,44 @@ +/* + * jSite - RedirectFileEntry.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +public class RedirectFileEntry extends FileEntry { + + final String targetURI; + + public RedirectFileEntry(String filename, String contentType, String targetURI) { + super(filename, contentType); + this.targetURI = targetURI; + } + + /** + * {@inheritDoc} + */ + @Override + public String getName() { + return "redirect"; + } + + /** + * @return Returns the targetURI. + */ + public String getTargetURI() { + return targetURI; + } +} \ No newline at end of file diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/ReturnType.java b/src/main/java/de/todesbaum/util/freenet/fcp2/ReturnType.java new file mode 100644 index 0000000..6f2ed7a --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/ReturnType.java @@ -0,0 +1,38 @@ +/* + * jSite - ReturnType.java - Copyright © 2008–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * Enumeration for the different return types a {@link ClientGet} request can + * have. + * + * @author David ‘Bombe’ Roden <bombe@freenetproject.org> + */ +public enum ReturnType { + + /** The data is returned as payload. */ + direct, + + /** The data is written to disk. */ + disk, + + /** The data is not returned at all. */ + none + +} diff --git a/src/main/java/de/todesbaum/util/freenet/fcp2/Verbosity.java b/src/main/java/de/todesbaum/util/freenet/fcp2/Verbosity.java new file mode 100644 index 0000000..e59b6a3 --- /dev/null +++ b/src/main/java/de/todesbaum/util/freenet/fcp2/Verbosity.java @@ -0,0 +1,50 @@ +/* + * jSite - Verbosity.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.freenet.fcp2; + +/** + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public final class Verbosity { + + public static final Verbosity PROGRESS = new Verbosity(1); + public static final Verbosity COMPRESSION = new Verbosity(512); + + public static final Verbosity NONE = new Verbosity(0); + public static final Verbosity ALL = new Verbosity(PROGRESS, COMPRESSION); + + private final int value; + + private Verbosity(int value) { + this.value = value; + } + + private Verbosity(Verbosity verbosity1, Verbosity verbosity2) { + this(verbosity1.value | verbosity2.value); + } + + /** + * @return Returns the value. + */ + public int getValue() { + return value; + } + +} diff --git a/src/main/java/de/todesbaum/util/image/IconLoader.java b/src/main/java/de/todesbaum/util/image/IconLoader.java new file mode 100644 index 0000000..ff6441a --- /dev/null +++ b/src/main/java/de/todesbaum/util/image/IconLoader.java @@ -0,0 +1,53 @@ +/* + * jSite - IconLoader.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.image; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.swing.Icon; +import javax.swing.ImageIcon; + +/** + * @author David Roden + * @version $Id$ + */ +public class IconLoader { + + public static Icon loadIcon(String resourceName) { + try { + InputStream resourceStream = IconLoader.class.getResourceAsStream(resourceName); + if (resourceStream == null) { + return null; + } + ByteArrayOutputStream imageOutput = new ByteArrayOutputStream(); + byte[] buffer = new byte[16384]; + int r = 0; + while ((r = resourceStream.read(buffer)) != -1) { + imageOutput.write(buffer, 0, r); + } + imageOutput.flush(); + return new ImageIcon(imageOutput.toByteArray()); + } catch (IOException e) { + } + return null; + } + +} diff --git a/src/main/java/de/todesbaum/util/io/Closer.java b/src/main/java/de/todesbaum/util/io/Closer.java new file mode 100644 index 0000000..67f7c57 --- /dev/null +++ b/src/main/java/de/todesbaum/util/io/Closer.java @@ -0,0 +1,189 @@ +/* + * jSite - Closer.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.ServerSocket; +import java.net.Socket; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * Helper class that can close all kinds of resources without throwing exception + * so that clean-up code can be written with less code. All methods check that + * the given resource is not null before invoking the close() + * method of the respective type. + * + * @author David ‘Bombe&squo; + * Roden + * @version $Id$ + */ +public class Closer { + + /** + * Closes the given result set. + * + * @param resultSet + * The result set to close + * @see ResultSet#close() + */ + public static void close(ResultSet resultSet) { + if (resultSet != null) { + try { + resultSet.close(); + } catch (SQLException ioe1) { + } + } + } + + /** + * Closes the given statement. + * + * @param statement + * The statement to close + * @see Statement#close() + */ + public static void close(Statement statement) { + if (statement != null) { + try { + statement.close(); + } catch (SQLException ioe1) { + } + } + } + + /** + * Closes the given connection. + * + * @param connection + * The connection to close + * @see Connection#close() + */ + public static void close(Connection connection) { + if (connection != null) { + try { + connection.close(); + } catch (SQLException ioe1) { + } + } + } + + /** + * Closes the given server socket. + * + * @param serverSocket + * The server socket to close + * @see ServerSocket#close() + */ + public static void close(ServerSocket serverSocket) { + if (serverSocket != null) { + try { + serverSocket.close(); + } catch (IOException ioe1) { + } + } + } + + /** + * Closes the given socket. + * + * @param socket + * The socket to close + * @see Socket#close() + */ + public static void close(Socket socket) { + if (socket != null) { + try { + socket.close(); + } catch (IOException ioe1) { + } + } + } + + /** + * Closes the given input stream. + * + * @param inputStream + * The input stream to close + * @see InputStream#close() + */ + public static void close(InputStream inputStream) { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ioe1) { + } + } + } + + /** + * Closes the given output stream. + * + * @param outputStream + * The output stream to close + * @see OutputStream#close() + */ + public static void close(OutputStream outputStream) { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException ioe1) { + } + } + } + + /** + * Closes the given reader. + * + * @param reader + * The reader to close + * @see Reader#close() + */ + public static void close(Reader reader) { + if (reader != null) { + try { + reader.close(); + } catch (IOException ioe1) { + } + } + } + + /** + * Closes the given writer. + * + * @param writer + * The write to close + * @see Writer#close() + */ + public static void close(Writer writer) { + if (writer != null) { + try { + writer.close(); + } catch (IOException ioe1) { + } + } + } + +} diff --git a/src/main/java/de/todesbaum/util/io/LineInputStream.java b/src/main/java/de/todesbaum/util/io/LineInputStream.java new file mode 100644 index 0000000..4fe0d07 --- /dev/null +++ b/src/main/java/de/todesbaum/util/io/LineInputStream.java @@ -0,0 +1,63 @@ +/* + * jSite - LineInputStream.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.io; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + + +/** + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class LineInputStream extends FilterInputStream { + + private boolean skipLinefeed = false; + private StringBuffer lineBuffer = new StringBuffer(); + + /** + * @param in + */ + public LineInputStream(InputStream in) { + super(in); + } + + public synchronized String readLine() throws IOException { + lineBuffer.setLength(0); + int c = 0; + while (c != -1) { + c = read(); + if ((c == -1) && lineBuffer.length() == 0) + return null; + if (skipLinefeed && (c == '\n')) { + skipLinefeed = false; + continue; + } + skipLinefeed = (c == '\r'); + if ((c == '\r') || (c == '\n')) { + c = -1; + } else { + lineBuffer.append((char) c); + } + } + return lineBuffer.toString(); + } + +} diff --git a/src/main/java/de/todesbaum/util/io/StreamCopier.java b/src/main/java/de/todesbaum/util/io/StreamCopier.java new file mode 100644 index 0000000..c7d99de --- /dev/null +++ b/src/main/java/de/todesbaum/util/io/StreamCopier.java @@ -0,0 +1,235 @@ +/* + * jSite - StreamCopier.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.io; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.EventListener; + +/** + * Copies input from an {@link InputStream} to an {@link OutputStream}. + * + * @author David Roden + * @version $Id$ + */ +public class StreamCopier { + + /** + * The default size of the buffer. + */ + private static final int BUFFER_SIZE = 64 * 1024; + + /** + * The {@link InputStream} to read from. + */ + private InputStream inputStream; + + /** + * The {@link OutputStream} to write to. + */ + private OutputStream outputStream; + + /** + * The number of bytes to copy. + */ + private long length; + + /** + * The size of the buffer. + */ + private int bufferSize; + + /** + * Creates a new StreamCopier with the specified parameters and the default + * buffer size. + * + * @param inputStream + * The {@link InputStream} to read from + * @param outputStream + * The {@link OutputStream} to write to + * @param length + * The number of bytes to copy + */ + public StreamCopier(InputStream inputStream, OutputStream outputStream, long length) { + this(inputStream, outputStream, length, BUFFER_SIZE); + } + + /** + * Creates a new StreamCopier with the specified parameters and the default + * buffer size. + * + * @param inputStream + * The {@link InputStream} to read from + * @param outputStream + * The {@link OutputStream} to write to + * @param length + * The number of bytes to copy + * @param bufferSize + * The number of bytes to copy at a time + */ + public StreamCopier(InputStream inputStream, OutputStream outputStream, long length, int bufferSize) { + this.inputStream = inputStream; + this.outputStream = outputStream; + this.length = length; + this.bufferSize = bufferSize; + } + + /** + * Copies the stream data. If the input stream is depleted before the + * requested number of bytes have been read an {@link EOFException} is + * thrown. + * + * @throws EOFException + * if the input stream is depleted before the requested number + * of bytes has been read + * @throws IOException + * if an I/O error occurs + */ + public void copy() throws EOFException, IOException { + copy(inputStream, outputStream, length, bufferSize); + } + + /** + * Copies the stream data. If the input stream is depleted before the + * requested number of bytes have been read an {@link EOFException} is + * thrown. + * + * @param progressListener + * The progress listener (may be {@code null}) + * @throws EOFException + * if the input stream is depleted before the requested number + * of bytes has been read + * @throws IOException + * if an I/O error occurs + */ + public void copy(ProgressListener progressListener) throws EOFException, IOException { + copy(inputStream, outputStream, length, bufferSize, progressListener); + } + + /** + * Copies 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 + * @throws IOException + * if an I/O exception occurs + */ + public static void copy(InputStream inputStream, OutputStream outputStream, long length) throws IOException { + copy(inputStream, outputStream, length, BUFFER_SIZE); + } + + /** + * Copies 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 + * The input stream to read from + * @param outputStream + * The output stream to write to + * @param length + * The number of bytes to copy + * @param bufferSize + * The size of the copy buffer + * @throws IOException + * if an I/O exception occurs + */ + public static void copy(InputStream inputStream, OutputStream outputStream, long length, int bufferSize) throws IOException { + copy(inputStream, outputStream, length, bufferSize, null); + } + + /** + * Copies 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) { + int read = inputStream.read(buffer, 0, (int) Math.min(Integer.MAX_VALUE, Math.min(bufferSize, remaining))); + if (read == -1) { + throw new EOFException(); + } + outputStream.write(buffer, 0, read); + remaining -= read; + if (progressListener != null) { + progressListener.onProgress(length - remaining, length); + } + } + } + + /** + * Interface for objects that want to be notified about the progress of a + * {@link StreamCopier#copy()} operation. + * + * @author David ‘Bombe’ Roden <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/main/java/de/todesbaum/util/io/TeeOutputStream.java b/src/main/java/de/todesbaum/util/io/TeeOutputStream.java new file mode 100644 index 0000000..78860dc --- /dev/null +++ b/src/main/java/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/main/java/de/todesbaum/util/io/TempFileInputStream.java b/src/main/java/de/todesbaum/util/io/TempFileInputStream.java new file mode 100644 index 0000000..43e2047 --- /dev/null +++ b/src/main/java/de/todesbaum/util/io/TempFileInputStream.java @@ -0,0 +1,57 @@ +/* + * jSite - TempFileInputStream.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.io; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class TempFileInputStream extends FileInputStream { + + private File tempFile; + + /** + * @param name + * @throws FileNotFoundException + */ + public TempFileInputStream(String name) throws FileNotFoundException { + this(new File(name)); + } + + /** + * @param file + * @throws FileNotFoundException + */ + public TempFileInputStream(File file) throws FileNotFoundException { + super(file); + tempFile = file; + } + + @Override + public void close() throws IOException { + super.close(); + tempFile.delete(); + } + +} diff --git a/src/main/java/de/todesbaum/util/mime/DefaultMIMETypes.java b/src/main/java/de/todesbaum/util/mime/DefaultMIMETypes.java new file mode 100644 index 0000000..9e380a5 --- /dev/null +++ b/src/main/java/de/todesbaum/util/mime/DefaultMIMETypes.java @@ -0,0 +1,834 @@ +/* taken from freenet (http://www.freenetproject.org/) */ +package de.todesbaum.util.mime; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +/** + * Holds the default MIME types. + */ +public class DefaultMIMETypes { + + /** Default MIME type - what to set it to if we don't know any better */ + public static final String DEFAULT_MIME_TYPE = "application/octet-stream"; + + /** MIME types: number -> name */ + private static List mimeTypesByNumber = new Vector(); + + /** MIME types: name -> number */ + private static Map mimeTypesByName = new HashMap(); + + /** MIME types by extension. One extension maps to one MIME type, but not necessarily + * the other way around. */ + private static Map mimeTypesByExtension = new HashMap(); + + /** Primary extension by MIME type number. */ + private static Map primaryExtensionByMimeNumber = new HashMap(); + + /** + * Add a MIME type, without any extensions. + * @param number The number of the MIME type for compression. This *must not change* + * for a given type, or the metadata format will be affected. + * @param type The actual MIME type string. Do not include ;charset= etc; these are + * parameters and there is a separate mechanism for them. + */ + protected static synchronized void addMIMEType(short number, String type) { + if(mimeTypesByNumber.size() > number) { + String s = mimeTypesByNumber.get(number); + if(s != null) throw new IllegalArgumentException("Already used: "+number); + } else { + mimeTypesByNumber.add(number, null); + } + mimeTypesByNumber.set(number, type); + mimeTypesByName.put(type, new Short(number)); + } + + /** + * Add a MIME type. + * @param number The number of the MIME type for compression. This *must not change* + * for a given type, or the metadata format will be affected. + * @param type The actual MIME type string. Do not include ;charset= etc; these are + * parameters and there is a separate mechanism for them. + * @param extensions An array of common extensions for files of this type. Must be + * unique for the type. + */ + protected static synchronized void addMIMEType(short number, String type, String[] extensions, String outExtension) { + addMIMEType(number, type); + Short t = new Short(number); + if(extensions != null) { + for(int i=0;i mimeTypesByNumber.size() || x < 0) + return null; + return mimeTypesByNumber.get(x); + } + + /** + * Get the number of a MIME type, or -1 if it is not in the table of known MIME + * types, in which case it will have to be sent uncompressed. + */ + public static short byName(String s) { + Short x = mimeTypesByName.get(s); + if(x != null) return x.shortValue(); + return -1; + } + + /* From toad's /etc/mime.types + * cat /etc/mime.types | sed "/^$/d;/#/d" | tr --squeeze '\t' ' ' | + * (y=0; while read x; do echo "$x" | + * sed -n "s/^\([^ ]*\)$/addMIMEType\($y, \"\1\"\);/p;s/^\([^ (),]\+\) \(.*\)$/addMIMEType\($y, \"\1\", \"\2\"\);/p;"; y=$((y+1)); done) + */ + + static { + addMIMEType((short) 0, "application/activemessage"); + addMIMEType((short) 1, "application/andrew-inset", "ez"); + addMIMEType((short) 2, "application/applefile"); + addMIMEType((short) 3, "application/atomicmail"); + addMIMEType((short) 4, "application/batch-SMTP"); + addMIMEType((short) 5, "application/beep+xml"); + addMIMEType((short) 6, "application/cals-1840"); + addMIMEType((short) 7, "application/commonground"); + addMIMEType((short) 8, "application/cu-seeme", "cu"); + addMIMEType((short) 9, "application/cybercash"); + addMIMEType((short) 10, "application/dca-rft"); + addMIMEType((short) 11, "application/dec-dx"); + addMIMEType((short) 12, "application/docbook+xml"); + addMIMEType((short) 13, "application/dsptype", "tsp"); + addMIMEType((short) 14, "application/dvcs"); + addMIMEType((short) 15, "application/edi-consent"); + addMIMEType((short) 16, "application/edi-x12"); + addMIMEType((short) 17, "application/edifact"); + addMIMEType((short) 18, "application/eshop"); + addMIMEType((short) 19, "application/font-tdpfr"); + addMIMEType((short) 20, "application/futuresplash", "spl"); + addMIMEType((short) 21, "application/ghostview"); + addMIMEType((short) 22, "application/hta", "hta"); + addMIMEType((short) 23, "application/http"); + addMIMEType((short) 24, "application/hyperstudio"); + addMIMEType((short) 25, "application/iges"); + addMIMEType((short) 26, "application/index"); + addMIMEType((short) 27, "application/index.cmd"); + addMIMEType((short) 28, "application/index.obj"); + addMIMEType((short) 29, "application/index.response"); + addMIMEType((short) 30, "application/index.vnd"); + addMIMEType((short) 31, "application/iotp"); + addMIMEType((short) 32, "application/ipp"); + addMIMEType((short) 33, "application/isup"); + addMIMEType((short) 34, "application/java-archive", "jar"); + addMIMEType((short) 35, "application/java-serialized-object", "ser"); + addMIMEType((short) 36, "application/java-vm", "class"); + addMIMEType((short) 37, "application/mac-binhex40", "hqx"); + addMIMEType((short) 38, "application/mac-compactpro", "cpt"); + addMIMEType((short) 39, "application/macwriteii"); + addMIMEType((short) 40, "application/marc"); + addMIMEType((short) 41, "application/mathematica", "nb"); + addMIMEType((short) 42, "application/mathematica-old"); + addMIMEType((short) 43, "application/msaccess", "mdb"); + addMIMEType((short) 44, "application/msword", "doc dot"); + addMIMEType((short) 45, "application/news-message-id"); + addMIMEType((short) 46, "application/news-transmission"); + addMIMEType((short) 47, "application/ocsp-request"); + addMIMEType((short) 48, "application/ocsp-response"); + addMIMEType((short) 49, "application/octet-stream", "bin"); + addMIMEType((short) 50, "application/oda", "oda"); + addMIMEType((short) 51, "application/ogg", "ogg"); + addMIMEType((short) 52, "application/parityfec"); + addMIMEType((short) 53, "application/pdf", "pdf"); + addMIMEType((short) 54, "application/pgp-encrypted"); + addMIMEType((short) 55, "application/pgp-keys", "key"); + addMIMEType((short) 56, "application/pgp-signature", "pgp"); + addMIMEType((short) 57, "application/pics-rules", "prf"); + addMIMEType((short) 58, "application/pkcs10"); + addMIMEType((short) 59, "application/pkcs7-mime"); + addMIMEType((short) 60, "application/pkcs7-signature"); + addMIMEType((short) 61, "application/pkix-cert"); + addMIMEType((short) 62, "application/pkix-crl"); + addMIMEType((short) 63, "application/pkixcmp"); + addMIMEType((short) 64, "application/postscript", "ps ai eps"); + addMIMEType((short) 65, "application/prs.alvestrand.titrax-sheet"); + addMIMEType((short) 66, "application/prs.cww"); + addMIMEType((short) 67, "application/prs.nprend"); + addMIMEType((short) 68, "application/qsig"); + addMIMEType((short) 69, "application/rar", "rar"); + addMIMEType((short) 70, "application/rdf+xml", "rdf"); + addMIMEType((short) 71, "application/remote-printing"); + addMIMEType((short) 72, "application/riscos"); + addMIMEType((short) 73, "application/rss+xml", "rss"); + addMIMEType((short) 74, "application/rtf"); + addMIMEType((short) 75, "application/sdp"); + addMIMEType((short) 76, "application/set-payment"); + addMIMEType((short) 77, "application/set-payment-initiation"); + addMIMEType((short) 78, "application/set-registration"); + addMIMEType((short) 79, "application/set-registration-initiation"); + addMIMEType((short) 80, "application/sgml"); + addMIMEType((short) 81, "application/sgml-open-catalog"); + addMIMEType((short) 82, "application/sieve"); + addMIMEType((short) 83, "application/slate"); + addMIMEType((short) 84, "application/smil", "smi smil"); + addMIMEType((short) 85, "application/timestamp-query"); + addMIMEType((short) 86, "application/timestamp-reply"); + addMIMEType((short) 87, "application/vemmi"); + addMIMEType((short) 88, "application/whoispp-query"); + addMIMEType((short) 89, "application/whoispp-response"); + addMIMEType((short) 90, "application/wita"); + addMIMEType((short) 91, "application/wordperfect", "wpd"); + addMIMEType((short) 92, "application/wordperfect5.1", "wp5"); + addMIMEType((short) 93, "application/x400-bp"); + addMIMEType((short) 94, "application/xhtml+xml", "xhtml xht"); + addMIMEType((short) 95, "application/xml", "xml xsl"); + addMIMEType((short) 96, "application/xml-dtd"); + addMIMEType((short) 97, "application/xml-external-parsed-entity"); + addMIMEType((short) 98, "application/zip", "zip"); + addMIMEType((short) 99, "application/vnd.3M.Post-it-Notes"); + addMIMEType((short) 100, "application/vnd.accpac.simply.aso"); + addMIMEType((short) 101, "application/vnd.accpac.simply.imp"); + addMIMEType((short) 102, "application/vnd.acucobol"); + addMIMEType((short) 103, "application/vnd.aether.imp"); + addMIMEType((short) 104, "application/vnd.anser-web-certificate-issue-initiation"); + addMIMEType((short) 105, "application/vnd.anser-web-funds-transfer-initiation"); + addMIMEType((short) 106, "application/vnd.audiograph"); + addMIMEType((short) 107, "application/vnd.bmi"); + addMIMEType((short) 108, "application/vnd.businessobjects"); + addMIMEType((short) 109, "application/vnd.canon-cpdl"); + addMIMEType((short) 110, "application/vnd.canon-lips"); + addMIMEType((short) 111, "application/vnd.cinderella", "cdy"); + addMIMEType((short) 112, "application/vnd.claymore"); + addMIMEType((short) 113, "application/vnd.commerce-battelle"); + addMIMEType((short) 114, "application/vnd.commonspace"); + addMIMEType((short) 115, "application/vnd.comsocaller"); + addMIMEType((short) 116, "application/vnd.contact.cmsg"); + addMIMEType((short) 117, "application/vnd.cosmocaller"); + addMIMEType((short) 118, "application/vnd.ctc-posml"); + addMIMEType((short) 119, "application/vnd.cups-postscript"); + addMIMEType((short) 120, "application/vnd.cups-raster"); + addMIMEType((short) 121, "application/vnd.cups-raw"); + addMIMEType((short) 122, "application/vnd.cybank"); + addMIMEType((short) 123, "application/vnd.dna"); + addMIMEType((short) 124, "application/vnd.dpgraph"); + addMIMEType((short) 125, "application/vnd.dxr"); + addMIMEType((short) 126, "application/vnd.ecdis-update"); + addMIMEType((short) 127, "application/vnd.ecowin.chart"); + addMIMEType((short) 128, "application/vnd.ecowin.filerequest"); + addMIMEType((short) 129, "application/vnd.ecowin.fileupdate"); + addMIMEType((short) 130, "application/vnd.ecowin.series"); + addMIMEType((short) 131, "application/vnd.ecowin.seriesrequest"); + addMIMEType((short) 132, "application/vnd.ecowin.seriesupdate"); + addMIMEType((short) 133, "application/vnd.enliven"); + addMIMEType((short) 134, "application/vnd.epson.esf"); + addMIMEType((short) 135, "application/vnd.epson.msf"); + addMIMEType((short) 136, "application/vnd.epson.quickanime"); + addMIMEType((short) 137, "application/vnd.epson.salt"); + addMIMEType((short) 138, "application/vnd.epson.ssf"); + addMIMEType((short) 139, "application/vnd.ericsson.quickcall"); + addMIMEType((short) 140, "application/vnd.eudora.data"); + addMIMEType((short) 141, "application/vnd.fdf"); + addMIMEType((short) 142, "application/vnd.ffsns"); + addMIMEType((short) 143, "application/vnd.flographit"); + addMIMEType((short) 144, "application/vnd.framemaker"); + addMIMEType((short) 145, "application/vnd.fsc.weblaunch"); + addMIMEType((short) 146, "application/vnd.fujitsu.oasys"); + addMIMEType((short) 147, "application/vnd.fujitsu.oasys2"); + addMIMEType((short) 148, "application/vnd.fujitsu.oasys3"); + addMIMEType((short) 149, "application/vnd.fujitsu.oasysgp"); + addMIMEType((short) 150, "application/vnd.fujitsu.oasysprs"); + addMIMEType((short) 151, "application/vnd.fujixerox.ddd"); + addMIMEType((short) 152, "application/vnd.fujixerox.docuworks"); + addMIMEType((short) 153, "application/vnd.fujixerox.docuworks.binder"); + addMIMEType((short) 154, "application/vnd.fut-misnet"); + addMIMEType((short) 155, "application/vnd.grafeq"); + addMIMEType((short) 156, "application/vnd.groove-account"); + addMIMEType((short) 157, "application/vnd.groove-identity-message"); + addMIMEType((short) 158, "application/vnd.groove-injector"); + addMIMEType((short) 159, "application/vnd.groove-tool-message"); + addMIMEType((short) 160, "application/vnd.groove-tool-template"); + addMIMEType((short) 161, "application/vnd.groove-vcard"); + addMIMEType((short) 162, "application/vnd.hhe.lesson-player"); + addMIMEType((short) 163, "application/vnd.hp-HPGL"); + addMIMEType((short) 164, "application/vnd.hp-PCL"); + addMIMEType((short) 165, "application/vnd.hp-PCLXL"); + addMIMEType((short) 166, "application/vnd.hp-hpid"); + addMIMEType((short) 167, "application/vnd.hp-hps"); + addMIMEType((short) 168, "application/vnd.httphone"); + addMIMEType((short) 169, "application/vnd.hzn-3d-crossword"); + addMIMEType((short) 170, "application/vnd.ibm.MiniPay"); + addMIMEType((short) 171, "application/vnd.ibm.afplinedata"); + addMIMEType((short) 172, "application/vnd.ibm.modcap"); + addMIMEType((short) 173, "application/vnd.informix-visionary"); + addMIMEType((short) 174, "application/vnd.intercon.formnet"); + addMIMEType((short) 175, "application/vnd.intertrust.digibox"); + addMIMEType((short) 176, "application/vnd.intertrust.nncp"); + addMIMEType((short) 177, "application/vnd.intu.qbo"); + addMIMEType((short) 178, "application/vnd.intu.qfx"); + addMIMEType((short) 179, "application/vnd.irepository.package+xml"); + addMIMEType((short) 180, "application/vnd.is-xpr"); + addMIMEType((short) 181, "application/vnd.japannet-directory-service"); + addMIMEType((short) 182, "application/vnd.japannet-jpnstore-wakeup"); + addMIMEType((short) 183, "application/vnd.japannet-payment-wakeup"); + addMIMEType((short) 184, "application/vnd.japannet-registration"); + addMIMEType((short) 185, "application/vnd.japannet-registration-wakeup"); + addMIMEType((short) 186, "application/vnd.japannet-setstore-wakeup"); + addMIMEType((short) 187, "application/vnd.japannet-verification"); + addMIMEType((short) 188, "application/vnd.japannet-verification-wakeup"); + addMIMEType((short) 189, "application/vnd.koan"); + addMIMEType((short) 190, "application/vnd.lotus-1-2-3"); + addMIMEType((short) 191, "application/vnd.lotus-approach"); + addMIMEType((short) 192, "application/vnd.lotus-freelance"); + addMIMEType((short) 193, "application/vnd.lotus-notes"); + addMIMEType((short) 194, "application/vnd.lotus-organizer"); + addMIMEType((short) 195, "application/vnd.lotus-screencam"); + addMIMEType((short) 196, "application/vnd.lotus-wordpro"); + addMIMEType((short) 197, "application/vnd.mcd"); + addMIMEType((short) 198, "application/vnd.mediastation.cdkey"); + addMIMEType((short) 199, "application/vnd.meridian-slingshot"); + addMIMEType((short) 200, "application/vnd.mif"); + addMIMEType((short) 201, "application/vnd.minisoft-hp3000-save"); + addMIMEType((short) 202, "application/vnd.mitsubishi.misty-guard.trustweb"); + addMIMEType((short) 203, "application/vnd.mobius.daf"); + addMIMEType((short) 204, "application/vnd.mobius.dis"); + addMIMEType((short) 205, "application/vnd.mobius.msl"); + addMIMEType((short) 206, "application/vnd.mobius.plc"); + addMIMEType((short) 207, "application/vnd.mobius.txf"); + addMIMEType((short) 208, "application/vnd.motorola.flexsuite"); + addMIMEType((short) 209, "application/vnd.motorola.flexsuite.adsi"); + addMIMEType((short) 210, "application/vnd.motorola.flexsuite.fis"); + addMIMEType((short) 211, "application/vnd.motorola.flexsuite.gotap"); + addMIMEType((short) 212, "application/vnd.motorola.flexsuite.kmr"); + addMIMEType((short) 213, "application/vnd.motorola.flexsuite.ttc"); + addMIMEType((short) 214, "application/vnd.motorola.flexsuite.wem"); + addMIMEType((short) 215, "application/vnd.mozilla.xul+xml", "xul"); + addMIMEType((short) 216, "application/vnd.ms-artgalry"); + addMIMEType((short) 217, "application/vnd.ms-asf"); + addMIMEType((short) 218, "application/vnd.ms-excel", "xls xlb xlt"); + addMIMEType((short) 219, "application/vnd.ms-lrm"); + addMIMEType((short) 220, "application/vnd.ms-pki.seccat", "cat"); + addMIMEType((short) 221, "application/vnd.ms-pki.stl", "stl"); + addMIMEType((short) 222, "application/vnd.ms-powerpoint", "ppt pps"); + addMIMEType((short) 223, "application/vnd.ms-project"); + addMIMEType((short) 224, "application/vnd.ms-tnef"); + addMIMEType((short) 225, "application/vnd.ms-works"); + addMIMEType((short) 226, "application/vnd.mseq"); + addMIMEType((short) 227, "application/vnd.msign"); + addMIMEType((short) 228, "application/vnd.music-niff"); + addMIMEType((short) 229, "application/vnd.musician"); + addMIMEType((short) 230, "application/vnd.netfpx"); + addMIMEType((short) 231, "application/vnd.noblenet-directory"); + addMIMEType((short) 232, "application/vnd.noblenet-sealer"); + addMIMEType((short) 233, "application/vnd.noblenet-web"); + addMIMEType((short) 234, "application/vnd.novadigm.EDM"); + addMIMEType((short) 235, "application/vnd.novadigm.EDX"); + addMIMEType((short) 236, "application/vnd.novadigm.EXT"); + addMIMEType((short) 237, "application/vnd.oasis.opendocument.chart", "odc"); + addMIMEType((short) 238, "application/vnd.oasis.opendocument.database", "odb"); + addMIMEType((short) 239, "application/vnd.oasis.opendocument.formula", "odf"); + addMIMEType((short) 240, "application/vnd.oasis.opendocument.graphics", "odg"); + addMIMEType((short) 241, "application/vnd.oasis.opendocument.graphics-template", "otg"); + addMIMEType((short) 242, "application/vnd.oasis.opendocument.image", "odi"); + addMIMEType((short) 243, "application/vnd.oasis.opendocument.presentation", "odp"); + addMIMEType((short) 244, "application/vnd.oasis.opendocument.presentation-template", "otp"); + addMIMEType((short) 245, "application/vnd.oasis.opendocument.spreadsheet", "ods"); + addMIMEType((short) 246, "application/vnd.oasis.opendocument.spreadsheet-template", "ots"); + addMIMEType((short) 247, "application/vnd.oasis.opendocument.text", "odt"); + addMIMEType((short) 248, "application/vnd.oasis.opendocument.text-master", "odm"); + addMIMEType((short) 249, "application/vnd.oasis.opendocument.text-template", "ott"); + addMIMEType((short) 250, "application/vnd.oasis.opendocument.text-web", "oth"); + addMIMEType((short) 251, "application/vnd.osa.netdeploy"); + addMIMEType((short) 252, "application/vnd.palm"); + addMIMEType((short) 253, "application/vnd.pg.format"); + addMIMEType((short) 254, "application/vnd.pg.osasli"); + addMIMEType((short) 255, "application/vnd.powerbuilder6"); + addMIMEType((short) 256, "application/vnd.powerbuilder6-s"); + addMIMEType((short) 257, "application/vnd.powerbuilder7"); + addMIMEType((short) 258, "application/vnd.powerbuilder7-s"); + addMIMEType((short) 259, "application/vnd.powerbuilder75"); + addMIMEType((short) 260, "application/vnd.powerbuilder75-s"); + addMIMEType((short) 261, "application/vnd.previewsystems.box"); + addMIMEType((short) 262, "application/vnd.publishare-delta-tree"); + addMIMEType((short) 263, "application/vnd.pvi.ptid1"); + addMIMEType((short) 264, "application/vnd.pwg-xhtml-print+xml"); + addMIMEType((short) 265, "application/vnd.rapid"); + addMIMEType((short) 266, "application/vnd.rim.cod", "cod"); + addMIMEType((short) 267, "application/vnd.s3sms"); + addMIMEType((short) 268, "application/vnd.seemail"); + addMIMEType((short) 269, "application/vnd.shana.informed.formdata"); + addMIMEType((short) 270, "application/vnd.shana.informed.formtemplate"); + addMIMEType((short) 271, "application/vnd.shana.informed.interchange"); + addMIMEType((short) 272, "application/vnd.shana.informed.package"); + addMIMEType((short) 273, "application/vnd.smaf", "mmf"); + addMIMEType((short) 274, "application/vnd.sss-cod"); + addMIMEType((short) 275, "application/vnd.sss-dtf"); + addMIMEType((short) 276, "application/vnd.sss-ntf"); + addMIMEType((short) 277, "application/vnd.stardivision.calc", "sdc"); + addMIMEType((short) 278, "application/vnd.stardivision.draw", "sda"); + addMIMEType((short) 279, "application/vnd.stardivision.impress", "sdd sdp"); + addMIMEType((short) 280, "application/vnd.stardivision.math", "smf"); + addMIMEType((short) 281, "application/vnd.stardivision.writer", "sdw vor"); + addMIMEType((short) 282, "application/vnd.stardivision.writer-global", "sgl"); + addMIMEType((short) 283, "application/vnd.street-stream"); + addMIMEType((short) 284, "application/vnd.sun.xml.calc", "sxc"); + addMIMEType((short) 285, "application/vnd.sun.xml.calc.template", "stc"); + addMIMEType((short) 286, "application/vnd.sun.xml.draw", "sxd"); + addMIMEType((short) 287, "application/vnd.sun.xml.draw.template", "std"); + addMIMEType((short) 288, "application/vnd.sun.xml.impress", "sxi"); + addMIMEType((short) 289, "application/vnd.sun.xml.impress.template", "sti"); + addMIMEType((short) 290, "application/vnd.sun.xml.math", "sxm"); + addMIMEType((short) 291, "application/vnd.sun.xml.writer", "sxw"); + addMIMEType((short) 292, "application/vnd.sun.xml.writer.global", "sxg"); + addMIMEType((short) 293, "application/vnd.sun.xml.writer.template", "stw"); + addMIMEType((short) 294, "application/vnd.svd"); + addMIMEType((short) 295, "application/vnd.swiftview-ics"); + addMIMEType((short) 296, "application/vnd.symbian.install", "sis"); + addMIMEType((short) 297, "application/vnd.triscape.mxs"); + addMIMEType((short) 298, "application/vnd.trueapp"); + addMIMEType((short) 299, "application/vnd.truedoc"); + addMIMEType((short) 300, "application/vnd.tve-trigger"); + addMIMEType((short) 301, "application/vnd.ufdl"); + addMIMEType((short) 302, "application/vnd.uplanet.alert"); + addMIMEType((short) 303, "application/vnd.uplanet.alert-wbxml"); + addMIMEType((short) 304, "application/vnd.uplanet.bearer-choice"); + addMIMEType((short) 305, "application/vnd.uplanet.bearer-choice-wbxml"); + addMIMEType((short) 306, "application/vnd.uplanet.cacheop"); + addMIMEType((short) 307, "application/vnd.uplanet.cacheop-wbxml"); + addMIMEType((short) 308, "application/vnd.uplanet.channel"); + addMIMEType((short) 309, "application/vnd.uplanet.channel-wbxml"); + addMIMEType((short) 310, "application/vnd.uplanet.list"); + addMIMEType((short) 311, "application/vnd.uplanet.list-wbxml"); + addMIMEType((short) 312, "application/vnd.uplanet.listcmd"); + addMIMEType((short) 313, "application/vnd.uplanet.listcmd-wbxml"); + addMIMEType((short) 314, "application/vnd.uplanet.signal"); + addMIMEType((short) 315, "application/vnd.vcx"); + addMIMEType((short) 316, "application/vnd.vectorworks"); + addMIMEType((short) 317, "application/vnd.vidsoft.vidconference"); + addMIMEType((short) 318, "application/vnd.visio", "vsd"); + addMIMEType((short) 319, "application/vnd.vividence.scriptfile"); + addMIMEType((short) 320, "application/vnd.wap.sic"); + addMIMEType((short) 321, "application/vnd.wap.slc"); + addMIMEType((short) 322, "application/vnd.wap.wbxml", "wbxml"); + addMIMEType((short) 323, "application/vnd.wap.wmlc", "wmlc"); + addMIMEType((short) 324, "application/vnd.wap.wmlscriptc", "wmlsc"); + addMIMEType((short) 325, "application/vnd.webturbo"); + addMIMEType((short) 326, "application/vnd.wrq-hp3000-labelled"); + addMIMEType((short) 327, "application/vnd.wt.stf"); + addMIMEType((short) 328, "application/vnd.xara"); + addMIMEType((short) 329, "application/vnd.xfdl"); + addMIMEType((short) 330, "application/vnd.yellowriver-custom-menu"); + addMIMEType((short) 331, "application/x-123", "wk"); + addMIMEType((short) 332, "application/x-abiword", "abw"); + addMIMEType((short) 333, "application/x-apple-diskimage", "dmg"); + addMIMEType((short) 334, "application/x-bcpio", "bcpio"); + addMIMEType((short) 335, "application/x-bittorrent", "torrent"); + addMIMEType((short) 336, "application/x-cdf", "cdf"); + addMIMEType((short) 337, "application/x-cdlink", "vcd"); + addMIMEType((short) 338, "application/x-chess-pgn", "pgn"); + addMIMEType((short) 339, "application/x-core"); + addMIMEType((short) 340, "application/x-cpio", "cpio"); + addMIMEType((short) 341, "application/x-csh", "csh"); + addMIMEType((short) 342, "application/x-debian-package", "deb udeb"); + addMIMEType((short) 343, "application/x-director", "dcr dir dxr"); + addMIMEType((short) 344, "application/x-dms", "dms"); + addMIMEType((short) 345, "application/x-doom", "wad"); + addMIMEType((short) 346, "application/x-dvi", "dvi"); + addMIMEType((short) 347, "application/x-executable"); + addMIMEType((short) 348, "application/x-flac", "flac"); + addMIMEType((short) 349, "application/x-font", "pfa pfb gsf pcf pcf.Z"); + addMIMEType((short) 350, "application/x-freemind", "mm"); + addMIMEType((short) 351, "application/x-futuresplash", "spl"); + addMIMEType((short) 352, "application/x-gnumeric", "gnumeric"); + addMIMEType((short) 353, "application/x-go-sgf", "sgf"); + addMIMEType((short) 354, "application/x-graphing-calculator", "gcf"); + addMIMEType((short) 355, "application/x-gtar", "gtar tgz taz"); + addMIMEType((short) 356, "application/x-hdf", "hdf"); + addMIMEType((short) 357, "application/x-httpd-php", "phtml pht php"); + addMIMEType((short) 358, "application/x-httpd-php-source", "phps"); + addMIMEType((short) 359, "application/x-httpd-php3", "php3"); + addMIMEType((short) 360, "application/x-httpd-php3-preprocessed", "php3p"); + addMIMEType((short) 361, "application/x-httpd-php4", "php4"); + addMIMEType((short) 362, "application/x-ica", "ica"); + addMIMEType((short) 363, "application/x-internet-signup", "ins isp"); + addMIMEType((short) 364, "application/x-iphone", "iii"); + addMIMEType((short) 365, "application/x-iso9660-image", "iso"); + addMIMEType((short) 366, "application/x-java-applet"); + addMIMEType((short) 367, "application/x-java-bean"); + addMIMEType((short) 368, "application/x-java-jnlp-file", "jnlp"); + addMIMEType((short) 369, "application/x-javascript", "js"); + addMIMEType((short) 370, "application/x-jmol", "jmz"); + addMIMEType((short) 371, "application/x-kchart", "chrt"); + addMIMEType((short) 372, "application/x-kdelnk"); + addMIMEType((short) 373, "application/x-killustrator", "kil"); + addMIMEType((short) 374, "application/x-koan", "skp skd skt skm"); + addMIMEType((short) 375, "application/x-kpresenter", "kpr kpt"); + addMIMEType((short) 376, "application/x-kspread", "ksp"); + addMIMEType((short) 377, "application/x-kword", "kwd kwt"); + addMIMEType((short) 378, "application/x-latex", "latex"); + addMIMEType((short) 379, "application/x-lha", "lha"); + addMIMEType((short) 380, "application/x-lzh", "lzh"); + addMIMEType((short) 381, "application/x-lzx", "lzx"); + addMIMEType((short) 382, "application/x-maker", "frm maker frame fm fb book fbdoc"); + addMIMEType((short) 383, "application/x-mif", "mif"); + addMIMEType((short) 384, "application/x-ms-wmd", "wmd"); + addMIMEType((short) 385, "application/x-ms-wmz", "wmz"); + addMIMEType((short) 386, "application/x-msdos-program", "com exe bat dll"); + addMIMEType((short) 387, "application/x-msi", "msi"); + addMIMEType((short) 388, "application/x-netcdf", "nc"); + addMIMEType((short) 389, "application/x-ns-proxy-autoconfig", "pac"); + addMIMEType((short) 390, "application/x-nwc", "nwc"); + addMIMEType((short) 391, "application/x-object", "o"); + addMIMEType((short) 392, "application/x-oz-application", "oza"); + addMIMEType((short) 393, "application/x-pkcs7-certreqresp", "p7r"); + addMIMEType((short) 394, "application/x-pkcs7-crl", "crl"); + addMIMEType((short) 395, "application/x-python-code", "pyc pyo"); + addMIMEType((short) 396, "application/x-quicktimeplayer", "qtl"); + addMIMEType((short) 397, "application/x-redhat-package-manager", "rpm"); + addMIMEType((short) 398, "application/x-rx"); + addMIMEType((short) 399, "application/x-sh", "sh"); + addMIMEType((short) 400, "application/x-shar", "shar"); + addMIMEType((short) 401, "application/x-shellscript"); + addMIMEType((short) 402, "application/x-shockwave-flash", "swf swfl"); + addMIMEType((short) 403, "application/x-stuffit", "sit"); + addMIMEType((short) 404, "application/x-sv4cpio", "sv4cpio"); + addMIMEType((short) 405, "application/x-sv4crc", "sv4crc"); + addMIMEType((short) 406, "application/x-tar", "tar"); + addMIMEType((short) 407, "application/x-tcl", "tcl"); + addMIMEType((short) 408, "application/x-tex-gf", "gf"); + addMIMEType((short) 409, "application/x-tex-pk", "pk"); + addMIMEType((short) 410, "application/x-texinfo", "texinfo texi"); + addMIMEType((short) 411, "application/x-trash", "~ % bak old sik"); + addMIMEType((short) 412, "application/x-troff", "t tr roff"); + addMIMEType((short) 413, "application/x-troff-man", "man"); + addMIMEType((short) 414, "application/x-troff-me", "me"); + addMIMEType((short) 415, "application/x-troff-ms", "ms"); + addMIMEType((short) 416, "application/x-ustar", "ustar"); + addMIMEType((short) 417, "application/x-videolan"); + addMIMEType((short) 418, "application/x-wais-source", "src"); + addMIMEType((short) 419, "application/x-wingz", "wz"); + addMIMEType((short) 420, "application/x-x509-ca-cert", "crt"); + addMIMEType((short) 421, "application/x-xcf", "xcf"); + addMIMEType((short) 422, "application/x-xfig", "fig"); + addMIMEType((short) 423, "application/x-xpinstall", "xpi"); + addMIMEType((short) 424, "audio/32kadpcm"); + addMIMEType((short) 425, "audio/basic", "au snd"); + addMIMEType((short) 426, "audio/g.722.1"); + addMIMEType((short) 427, "audio/l16"); + addMIMEType((short) 428, "audio/midi", "mid midi kar"); + addMIMEType((short) 429, "audio/mp4a-latm"); + addMIMEType((short) 430, "audio/mpa-robust"); + addMIMEType((short) 431, "audio/mpeg", "mpga mpega mp2 mp3 m4a"); + addMIMEType((short) 432, "audio/mpegurl", "m3u"); + addMIMEType((short) 433, "audio/parityfec"); + addMIMEType((short) 434, "audio/prs.sid", "sid"); + addMIMEType((short) 435, "audio/telephone-event"); + addMIMEType((short) 436, "audio/tone"); + addMIMEType((short) 437, "audio/vnd.cisco.nse"); + addMIMEType((short) 438, "audio/vnd.cns.anp1"); + addMIMEType((short) 439, "audio/vnd.cns.inf1"); + addMIMEType((short) 440, "audio/vnd.digital-winds"); + addMIMEType((short) 441, "audio/vnd.everad.plj"); + addMIMEType((short) 442, "audio/vnd.lucent.voice"); + addMIMEType((short) 443, "audio/vnd.nortel.vbk"); + addMIMEType((short) 444, "audio/vnd.nuera.ecelp4800"); + addMIMEType((short) 445, "audio/vnd.nuera.ecelp7470"); + addMIMEType((short) 446, "audio/vnd.nuera.ecelp9600"); + addMIMEType((short) 447, "audio/vnd.octel.sbc"); + addMIMEType((short) 448, "audio/vnd.qcelp"); + addMIMEType((short) 449, "audio/vnd.rhetorex.32kadpcm"); + addMIMEType((short) 450, "audio/vnd.vmx.cvsd"); + addMIMEType((short) 451, "audio/x-aiff", "aif aiff aifc"); + addMIMEType((short) 452, "audio/x-gsm", "gsm"); + addMIMEType((short) 453, "audio/x-mpegurl", "m3u"); + addMIMEType((short) 454, "audio/x-ms-wma", "wma"); + addMIMEType((short) 455, "audio/x-ms-wax", "wax"); + addMIMEType((short) 456, "audio/x-pn-realaudio-plugin"); + addMIMEType((short) 457, "audio/x-pn-realaudio", "ra rm ram"); + addMIMEType((short) 458, "audio/x-realaudio", "ra"); + addMIMEType((short) 459, "audio/x-scpls", "pls"); + addMIMEType((short) 460, "audio/x-sd2", "sd2"); + addMIMEType((short) 461, "audio/x-wav", "wav"); + addMIMEType((short) 462, "chemical/x-alchemy", "alc"); + addMIMEType((short) 463, "chemical/x-cache", "cac cache"); + addMIMEType((short) 464, "chemical/x-cache-csf", "csf"); + addMIMEType((short) 465, "chemical/x-cactvs-binary", "cbin cascii ctab"); + addMIMEType((short) 466, "chemical/x-cdx", "cdx"); + addMIMEType((short) 467, "chemical/x-cerius", "cer"); + addMIMEType((short) 468, "chemical/x-chem3d", "c3d"); + addMIMEType((short) 469, "chemical/x-chemdraw", "chm"); + addMIMEType((short) 470, "chemical/x-cif", "cif"); + addMIMEType((short) 471, "chemical/x-cmdf", "cmdf"); + addMIMEType((short) 472, "chemical/x-cml", "cml"); + addMIMEType((short) 473, "chemical/x-compass", "cpa"); + addMIMEType((short) 474, "chemical/x-crossfire", "bsd"); + addMIMEType((short) 475, "chemical/x-csml", "csml csm"); + addMIMEType((short) 476, "chemical/x-ctx", "ctx"); + addMIMEType((short) 477, "chemical/x-cxf", "cxf cef"); + addMIMEType((short) 478, "chemical/x-embl-dl-nucleotide", "emb embl"); + addMIMEType((short) 479, "chemical/x-galactic-spc", "spc"); + addMIMEType((short) 480, "chemical/x-gamess-input", "inp gam gamin"); + addMIMEType((short) 481, "chemical/x-gaussian-checkpoint", "fch fchk"); + addMIMEType((short) 482, "chemical/x-gaussian-cube", "cub"); + addMIMEType((short) 483, "chemical/x-gaussian-input", "gau gjc gjf"); + addMIMEType((short) 484, "chemical/x-gaussian-log", "gal"); + addMIMEType((short) 485, "chemical/x-gcg8-sequence", "gcg"); + addMIMEType((short) 486, "chemical/x-genbank", "gen"); + addMIMEType((short) 487, "chemical/x-hin", "hin"); + addMIMEType((short) 488, "chemical/x-isostar", "istr ist"); + addMIMEType((short) 489, "chemical/x-jcamp-dx", "jdx dx"); + addMIMEType((short) 490, "chemical/x-kinemage", "kin"); + addMIMEType((short) 491, "chemical/x-macmolecule", "mcm"); + addMIMEType((short) 492, "chemical/x-macromodel-input", "mmd mmod"); + addMIMEType((short) 493, "chemical/x-mdl-molfile", "mol"); + addMIMEType((short) 494, "chemical/x-mdl-rdfile", "rd"); + addMIMEType((short) 495, "chemical/x-mdl-rxnfile", "rxn"); + addMIMEType((short) 496, "chemical/x-mdl-sdfile", "sd sdf"); + addMIMEType((short) 497, "chemical/x-mdl-tgf", "tgf"); + addMIMEType((short) 498, "chemical/x-mmcif", "mcif"); + addMIMEType((short) 499, "chemical/x-mol2", "mol2"); + addMIMEType((short) 500, "chemical/x-molconn-Z", "b"); + addMIMEType((short) 501, "chemical/x-mopac-graph", "gpt"); + addMIMEType((short) 502, "chemical/x-mopac-input", "mop mopcrt mpc dat zmt"); + addMIMEType((short) 503, "chemical/x-mopac-out", "moo"); + addMIMEType((short) 504, "chemical/x-mopac-vib", "mvb"); + addMIMEType((short) 505, "chemical/x-ncbi-asn1", "asn"); + addMIMEType((short) 506, "chemical/x-ncbi-asn1-ascii", "prt ent"); + addMIMEType((short) 507, "chemical/x-ncbi-asn1-binary", "val aso"); + addMIMEType((short) 508, "chemical/x-ncbi-asn1-spec", "asn"); + addMIMEType((short) 509, "chemical/x-pdb", "pdb ent"); + addMIMEType((short) 510, "chemical/x-rosdal", "ros"); + addMIMEType((short) 511, "chemical/x-swissprot", "sw"); + addMIMEType((short) 512, "chemical/x-vamas-iso14976", "vms"); + addMIMEType((short) 513, "chemical/x-vmd", "vmd"); + addMIMEType((short) 514, "chemical/x-xtel", "xtel"); + addMIMEType((short) 515, "chemical/x-xyz", "xyz"); + addMIMEType((short) 516, "image/cgm"); + addMIMEType((short) 517, "image/g3fax"); + addMIMEType((short) 518, "image/gif", "gif"); + addMIMEType((short) 519, "image/ief", "ief"); + addMIMEType((short) 520, "image/jpeg", "jpeg jpg jpe"); + addMIMEType((short) 521, "image/naplps"); + addMIMEType((short) 522, "image/pcx", "pcx"); + addMIMEType((short) 523, "image/png", "png"); + addMIMEType((short) 524, "image/prs.btif"); + addMIMEType((short) 525, "image/prs.pti"); + addMIMEType((short) 526, "image/svg+xml", "svg svgz"); + addMIMEType((short) 527, "image/tiff", "tiff tif"); + addMIMEType((short) 528, "image/vnd.cns.inf2"); + addMIMEType((short) 529, "image/vnd.djvu", "djvu djv"); + addMIMEType((short) 530, "image/vnd.dwg"); + addMIMEType((short) 531, "image/vnd.dxf"); + addMIMEType((short) 532, "image/vnd.fastbidsheet"); + addMIMEType((short) 533, "image/vnd.fpx"); + addMIMEType((short) 534, "image/vnd.fst"); + addMIMEType((short) 535, "image/vnd.fujixerox.edmics-mmr"); + addMIMEType((short) 536, "image/vnd.fujixerox.edmics-rlc"); + addMIMEType((short) 537, "image/vnd.mix"); + addMIMEType((short) 538, "image/vnd.net-fpx"); + addMIMEType((short) 539, "image/vnd.svf"); + addMIMEType((short) 540, "image/vnd.wap.wbmp", "wbmp"); + addMIMEType((short) 541, "image/vnd.xiff"); + addMIMEType((short) 542, "image/x-cmu-raster", "ras"); + addMIMEType((short) 543, "image/x-coreldraw", "cdr"); + addMIMEType((short) 544, "image/x-coreldrawpattern", "pat"); + addMIMEType((short) 545, "image/x-coreldrawtemplate", "cdt"); + addMIMEType((short) 546, "image/x-corelphotopaint", "cpt"); + addMIMEType((short) 547, "image/x-icon", "ico"); + addMIMEType((short) 548, "image/x-jg", "art"); + addMIMEType((short) 549, "image/x-jng", "jng"); + addMIMEType((short) 550, "image/x-ms-bmp", "bmp"); + addMIMEType((short) 551, "image/x-photoshop", "psd"); + addMIMEType((short) 552, "image/x-portable-anymap", "pnm"); + addMIMEType((short) 553, "image/x-portable-bitmap", "pbm"); + addMIMEType((short) 554, "image/x-portable-graymap", "pgm"); + addMIMEType((short) 555, "image/x-portable-pixmap", "ppm"); + addMIMEType((short) 556, "image/x-rgb", "rgb"); + addMIMEType((short) 557, "image/x-xbitmap", "xbm"); + addMIMEType((short) 558, "image/x-xpixmap", "xpm"); + addMIMEType((short) 559, "image/x-xwindowdump", "xwd"); + addMIMEType((short) 560, "inode/chardevice"); + addMIMEType((short) 561, "inode/blockdevice"); + addMIMEType((short) 562, "inode/directory-locked"); + addMIMEType((short) 563, "inode/directory"); + addMIMEType((short) 564, "inode/fifo"); + addMIMEType((short) 565, "inode/socket"); + addMIMEType((short) 566, "message/delivery-status"); + addMIMEType((short) 567, "message/disposition-notification"); + addMIMEType((short) 568, "message/external-body"); + addMIMEType((short) 569, "message/http"); + addMIMEType((short) 570, "message/s-http"); + addMIMEType((short) 571, "message/news"); + addMIMEType((short) 572, "message/partial"); + addMIMEType((short) 573, "message/rfc822"); + addMIMEType((short) 574, "model/iges", "igs iges"); + addMIMEType((short) 575, "model/mesh", "msh mesh silo"); + addMIMEType((short) 576, "model/vnd.dwf"); + addMIMEType((short) 577, "model/vnd.flatland.3dml"); + addMIMEType((short) 578, "model/vnd.gdl"); + addMIMEType((short) 579, "model/vnd.gs-gdl"); + addMIMEType((short) 580, "model/vnd.gtw"); + addMIMEType((short) 581, "model/vnd.mts"); + addMIMEType((short) 582, "model/vnd.vtu"); + addMIMEType((short) 583, "model/vrml", "wrl vrml"); + addMIMEType((short) 584, "multipart/alternative"); + addMIMEType((short) 585, "multipart/appledouble"); + addMIMEType((short) 586, "multipart/byteranges"); + addMIMEType((short) 587, "multipart/digest"); + addMIMEType((short) 588, "multipart/encrypted"); + addMIMEType((short) 589, "multipart/form-data"); + addMIMEType((short) 590, "multipart/header-set"); + addMIMEType((short) 591, "multipart/mixed"); + addMIMEType((short) 592, "multipart/parallel"); + addMIMEType((short) 593, "multipart/related"); + addMIMEType((short) 594, "multipart/report"); + addMIMEType((short) 595, "multipart/signed"); + addMIMEType((short) 596, "multipart/voice-message"); + addMIMEType((short) 597, "text/calendar", "ics icz"); + addMIMEType((short) 598, "text/comma-separated-values", "csv"); + addMIMEType((short) 599, "text/css", "css"); + addMIMEType((short) 600, "text/directory"); + addMIMEType((short) 601, "text/english"); + addMIMEType((short) 602, "text/enriched"); + addMIMEType((short) 603, "text/h323", "323"); + addMIMEType((short) 604, "text/html", "html htm shtml"); + addMIMEType((short) 605, "text/iuls", "uls"); + addMIMEType((short) 606, "text/mathml", "mml"); + addMIMEType((short) 607, "text/parityfec"); + addMIMEType((short) 608, "text/plain", "asc txt text diff pot"); + addMIMEType((short) 609, "text/prs.lines.tag"); + addMIMEType((short) 610, "text/x-psp", "psp"); + addMIMEType((short) 611, "text/rfc822-headers"); + addMIMEType((short) 612, "text/richtext", "rtx"); + addMIMEType((short) 613, "text/rtf", "rtf"); + addMIMEType((short) 614, "text/scriptlet", "sct wsc"); + addMIMEType((short) 615, "text/t140"); + addMIMEType((short) 616, "text/texmacs", "tm ts"); + addMIMEType((short) 617, "text/tab-separated-values", "tsv"); + addMIMEType((short) 618, "text/uri-list"); + addMIMEType((short) 619, "text/vnd.abc"); + addMIMEType((short) 620, "text/vnd.curl"); + addMIMEType((short) 621, "text/vnd.DMClientScript"); + addMIMEType((short) 622, "text/vnd.flatland.3dml"); + addMIMEType((short) 623, "text/vnd.fly"); + addMIMEType((short) 624, "text/vnd.fmi.flexstor"); + addMIMEType((short) 625, "text/vnd.in3d.3dml"); + addMIMEType((short) 626, "text/vnd.in3d.spot"); + addMIMEType((short) 627, "text/vnd.IPTC.NewsML"); + addMIMEType((short) 628, "text/vnd.IPTC.NITF"); + addMIMEType((short) 629, "text/vnd.latex-z"); + addMIMEType((short) 630, "text/vnd.motorola.reflex"); + addMIMEType((short) 631, "text/vnd.ms-mediapackage"); + addMIMEType((short) 632, "text/vnd.sun.j2me.app-descriptor", "jad"); + addMIMEType((short) 633, "text/vnd.wap.si"); + addMIMEType((short) 634, "text/vnd.wap.sl"); + addMIMEType((short) 635, "text/vnd.wap.wml", "wml"); + addMIMEType((short) 636, "text/vnd.wap.wmlscript", "wmls"); + addMIMEType((short) 637, "text/x-bibtex", "bib"); + addMIMEType((short) 638, "text/x-c++hdr", "h++ hpp hxx hh"); + addMIMEType((short) 639, "text/x-c++src", "c++ cpp cxx cc"); + addMIMEType((short) 640, "text/x-chdr", "h"); + addMIMEType((short) 641, "text/x-crontab"); + addMIMEType((short) 642, "text/x-csh", "csh"); + addMIMEType((short) 643, "text/x-csrc", "c"); + addMIMEType((short) 644, "text/x-haskell", "hs"); + addMIMEType((short) 645, "text/x-java", "java"); + addMIMEType((short) 646, "text/x-literate-haskell", "lhs"); + addMIMEType((short) 647, "text/x-makefile"); + addMIMEType((short) 648, "text/x-moc", "moc"); + addMIMEType((short) 649, "text/x-pascal", "p pas"); + addMIMEType((short) 650, "text/x-pcs-gcd", "gcd"); + addMIMEType((short) 651, "text/x-perl", "pl pm"); + addMIMEType((short) 652, "text/x-python", "py"); + addMIMEType((short) 653, "text/x-server-parsed-html"); + addMIMEType((short) 654, "text/x-setext", "etx"); + addMIMEType((short) 655, "text/x-sh", "sh"); + addMIMEType((short) 656, "text/x-tcl", "tcl tk"); + addMIMEType((short) 657, "text/x-tex", "tex ltx sty cls"); + addMIMEType((short) 658, "text/x-vcalendar", "vcs"); + addMIMEType((short) 659, "text/x-vcard", "vcf"); + addMIMEType((short) 660, "video/dl", "dl"); + addMIMEType((short) 661, "video/dv", "dif dv"); + addMIMEType((short) 662, "video/fli", "fli"); + addMIMEType((short) 663, "video/gl", "gl"); + addMIMEType((short) 664, "video/mpeg", "mpeg mpg mpe"); + addMIMEType((short) 665, "video/mp4", "mp4"); + addMIMEType((short) 666, "video/quicktime", "qt mov"); + addMIMEType((short) 667, "video/mp4v-es"); + addMIMEType((short) 668, "video/parityfec"); + addMIMEType((short) 669, "video/pointer"); + addMIMEType((short) 670, "video/vnd.fvt"); + addMIMEType((short) 671, "video/vnd.motorola.video"); + addMIMEType((short) 672, "video/vnd.motorola.videop"); + addMIMEType((short) 673, "video/vnd.mpegurl", "mxu"); + addMIMEType((short) 674, "video/vnd.mts"); + addMIMEType((short) 675, "video/vnd.nokia.interleaved-multimedia"); + addMIMEType((short) 676, "video/vnd.vivo"); + addMIMEType((short) 677, "video/x-la-asf", "lsf lsx"); + addMIMEType((short) 678, "video/x-mng", "mng"); + addMIMEType((short) 679, "video/x-ms-asf", "asf asx"); + addMIMEType((short) 680, "video/x-ms-wm", "wm"); + addMIMEType((short) 681, "video/x-ms-wmv", "wmv"); + addMIMEType((short) 682, "video/x-ms-wmx", "wmx"); + addMIMEType((short) 683, "video/x-ms-wvx", "wvx"); + addMIMEType((short) 684, "video/x-msvideo", "avi"); + addMIMEType((short) 685, "video/x-sgi-movie", "movie"); + addMIMEType((short) 686, "x-conference/x-cooltalk", "ice"); + addMIMEType((short) 687, "x-world/x-vrml", "vrm vrml wrl"); + } + + /** Guess a MIME type from a filename */ + public static String guessMIMEType(String arg) { + int x = arg.lastIndexOf('.'); + if(x == -1 || x == arg.length()-1) + return DEFAULT_MIME_TYPE; + String ext = arg.substring(x+1).toLowerCase(); + Short mimeIndexOb = mimeTypesByExtension.get(ext); + if(mimeIndexOb != null) { + return mimeTypesByNumber.get(mimeIndexOb.intValue()); + } + return DEFAULT_MIME_TYPE; + } + + public static String getExtension(String type) { + short typeNumber = byName(type); + if(typeNumber < 0) return null; + return primaryExtensionByMimeNumber.get(typeNumber); + } + + public static String[] getAllMIMETypes() { + return mimeTypesByNumber.toArray(new String[mimeTypesByNumber.size()]); + } + +} diff --git a/src/main/java/de/todesbaum/util/swing/SortedListModel.java b/src/main/java/de/todesbaum/util/swing/SortedListModel.java new file mode 100644 index 0000000..39a5ce2 --- /dev/null +++ b/src/main/java/de/todesbaum/util/swing/SortedListModel.java @@ -0,0 +1,250 @@ +/* + * jSite - SortedListModel.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.swing; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import javax.swing.AbstractListModel; + +/** + * @param + * The type of the elements + * @author David Roden <droden@gmail.com> + */ +public class SortedListModel> extends AbstractListModel implements List { + + /** The elements. */ + private List elements = new ArrayList(); + + /** + * {@inheritDoc} + */ + public int getSize() { + return size(); + } + + /** + * {@inheritDoc} + */ + public Object getElementAt(int index) { + return elements.get(index); + } + + /** + * {@inheritDoc} + */ + public void add(int index, T element) { + elements.add(index, element); + Collections.sort(elements); + fireContentsChanged(this, 0, size()); + } + + /** + * {@inheritDoc} + */ + public boolean add(T o) { + boolean result = elements.add(o); + Collections.sort(elements); + fireContentsChanged(this, 0, size()); + return result; + } + + /** + * {@inheritDoc} + */ + public boolean addAll(Collection c) { + boolean result = elements.addAll(c); + Collections.sort(elements); + fireContentsChanged(this, 0, size()); + return result; + } + + /** + * {@inheritDoc} + */ + public boolean addAll(int index, Collection c) { + boolean result = elements.addAll(index, c); + Collections.sort(elements); + fireContentsChanged(this, 0, size()); + return result; + } + + /** + * {@inheritDoc} + */ + public void clear() { + elements.clear(); + fireContentsChanged(this, 0, size()); + } + + /** + * {@inheritDoc} + */ + public boolean contains(Object o) { + return elements.contains(o); + } + + /** + * {@inheritDoc} + */ + public boolean containsAll(Collection c) { + return elements.containsAll(c); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(Object o) { + return elements.equals(o); + } + + /** + * {@inheritDoc} + */ + public T get(int index) { + return elements.get(index); + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return elements.hashCode(); + } + + /** + * {@inheritDoc} + */ + public int indexOf(Object o) { + return elements.indexOf(o); + } + + /** + * {@inheritDoc} + */ + public boolean isEmpty() { + return elements.isEmpty(); + } + + /** + * {@inheritDoc} + */ + public Iterator iterator() { + return elements.iterator(); + } + + /** + * {@inheritDoc} + */ + public int lastIndexOf(Object o) { + return elements.lastIndexOf(o); + } + + /** + * {@inheritDoc} + */ + public ListIterator listIterator() { + return elements.listIterator(); + } + + /** + * {@inheritDoc} + */ + public ListIterator listIterator(int index) { + return elements.listIterator(index); + } + + /** + * {@inheritDoc} + */ + public T remove(int index) { + fireContentsChanged(this, 0, size()); + return elements.remove(index); + } + + /** + * {@inheritDoc} + */ + public boolean remove(Object o) { + fireContentsChanged(this, 0, size()); + return elements.remove(o); + } + + /** + * {@inheritDoc} + */ + public boolean removeAll(Collection c) { + fireContentsChanged(this, 0, size()); + return elements.removeAll(c); + } + + /** + * {@inheritDoc} + */ + public boolean retainAll(Collection c) { + fireContentsChanged(this, 0, size()); + return elements.retainAll(c); + } + + /** + * {@inheritDoc} + */ + public T set(int index, T element) { + T result = elements.set(index, element); + Collections.sort(elements); + fireContentsChanged(this, 0, size()); + return result; + } + + /** + * {@inheritDoc} + */ + public int size() { + return elements.size(); + } + + /** + * {@inheritDoc} + */ + public List subList(int fromIndex, int toIndex) { + return elements.subList(fromIndex, toIndex); + } + + /** + * {@inheritDoc} + */ + public Object[] toArray() { + return elements.toArray(); + } + + /** + * {@inheritDoc} + */ + public U[] toArray(U[] a) { + return elements.toArray(a); + } + +} diff --git a/src/main/java/de/todesbaum/util/swing/TLabel.java b/src/main/java/de/todesbaum/util/swing/TLabel.java new file mode 100644 index 0000000..0393f5c --- /dev/null +++ b/src/main/java/de/todesbaum/util/swing/TLabel.java @@ -0,0 +1,93 @@ +/* + * jSite - TLabel.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.swing; + +import java.awt.Component; + +import javax.swing.Icon; +import javax.swing.JLabel; + +/** + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class TLabel extends JLabel { + + public TLabel() { + super(); + } + + public TLabel(int mnemonic, Component labelFor) { + super(); + setDisplayedMnemonic(mnemonic); + setLabelFor(labelFor); + } + + public TLabel(Icon image) { + super(image); + } + + public TLabel(Icon image, int mnemonic, Component labelFor) { + super(image); + setDisplayedMnemonic(mnemonic); + setLabelFor(labelFor); + } + + public TLabel(Icon image, int horizontalAlignment) { + super(image); + } + + public TLabel(Icon image, int horizontalAlignment, int mnemonic, Component labelFor) { + super(image); + setDisplayedMnemonic(mnemonic); + setLabelFor(labelFor); + } + + public TLabel(String text) { + super(text); + } + + public TLabel(String text, int mnemonic, Component labelFor) { + super(text); + setDisplayedMnemonic(mnemonic); + setLabelFor(labelFor); + setAlignmentX(0.0f); + } + + public TLabel(String text, Icon icon, int horizontalAlignment) { + super(text, icon, horizontalAlignment); + } + + public TLabel(String text, Icon icon, int horizontalAlignment, int mnemonic, Component labelFor) { + super(text, icon, horizontalAlignment); + setDisplayedMnemonic(mnemonic); + setLabelFor(labelFor); + } + + public TLabel(String text, int horizontalAlignment) { + super(text, horizontalAlignment); + } + + public TLabel(String text, int horizontalAlignment, int mnemonic, Component labelFor) { + super(text, horizontalAlignment); + setDisplayedMnemonic(mnemonic); + setLabelFor(labelFor); + } + +} diff --git a/src/main/java/de/todesbaum/util/swing/TWizard.java b/src/main/java/de/todesbaum/util/swing/TWizard.java new file mode 100644 index 0000000..464ac69 --- /dev/null +++ b/src/main/java/de/todesbaum/util/swing/TWizard.java @@ -0,0 +1,260 @@ +/* + * jSite - TWizard.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.swing; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Font; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; + +/** + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class TWizard extends JFrame implements WindowListener { + + protected List wizardListeners = new ArrayList(); + + private Action previousAction; + private Action nextAction; + private Action quitAction; + private JLabel pageIcon; + private JPanel pagePanel; + private JLabel pageHeading; + private JLabel pageDescription; + + @Override + protected void frameInit() { + super.frameInit(); + setResizable(false); + addWindowListener(this); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + createActions(); + + pageIcon = new JLabel(); + pageIcon.setVerticalAlignment(SwingConstants.TOP); + pageHeading = new JLabel(); + pageHeading.setFont(pageHeading.getFont().deriveFont(pageHeading.getFont().getSize() * 2.0f).deriveFont(Font.BOLD)); + pageDescription = new JLabel(); + + JPanel contentPane = new JPanel(new BorderLayout(12, 12)); + contentPane.setBorder(new EmptyBorder(12, 12, 12, 12)); + + JPanel topPanel = new JPanel(new BorderLayout(12, 12)); + contentPane.add(topPanel, BorderLayout.PAGE_START); + + topPanel.add(pageIcon, BorderLayout.LINE_START); + + JPanel textPanel = new JPanel(new BorderLayout(12, 12)); + topPanel.add(textPanel, BorderLayout.CENTER); + textPanel.add(pageHeading, BorderLayout.PAGE_START); + textPanel.add(pageDescription, BorderLayout.CENTER); + + pagePanel = new JPanel(new BorderLayout(12, 12)); + contentPane.add(pagePanel, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 12, 12)); + buttonPanel.setBorder(new EmptyBorder(-12, -12, -12, -12)); + buttonPanel.add(new JButton(previousAction)); + buttonPanel.add(new JButton(nextAction)); + buttonPanel.add(new JButton(quitAction)); + contentPane.add(buttonPanel, BorderLayout.PAGE_END); + + setContentPane(contentPane); + } + + @Override + public void pack() { + super.pack(); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + setLocation((screenSize.width - getWidth()) / 2, (screenSize.height - getHeight()) / 2); + // System.out.println("resized to: " + getWidth() + "x" + getHeight()); + } + + private void createActions() { + previousAction = new AbstractAction("Previous") { + public void actionPerformed(ActionEvent actionEvent) { + actionPrevious(); + } + }; + + nextAction = new AbstractAction("Next") { + public void actionPerformed(ActionEvent actionEvent) { + actionNext(); + } + }; + + quitAction = new AbstractAction("Quit") { + public void actionPerformed(ActionEvent actionEvent) { + actionQuit(); + } + }; + } + + public void addWizardListener(WizardListener wizardListener) { + wizardListeners.add(wizardListener); + } + + public void removeWizardListener(WizardListener wizardListener) { + wizardListeners.remove(wizardListener); + } + + protected void fireWizardPreviousPressed() { + for (WizardListener wizardListener: wizardListeners) { + wizardListener.wizardPreviousPressed(this); + } + } + + protected void fireWizardNextPressed() { + for (WizardListener wizardListener: wizardListeners) { + wizardListener.wizardNextPressed(this); + } + } + + protected void fireWizardQuitPressed() { + for (WizardListener wizardListener: wizardListeners) { + wizardListener.wizardQuitPressed(this); + } + } + + public void setIcon(Icon icon) { + pageIcon.setIcon(icon); + } + + public void setPage(TWizardPage page) { + setVisible(false); + pageHeading.setText(page.getHeading()); + pageDescription.setText(page.getDescription()); + if (pagePanel.getComponentCount() > 0) { + if (pagePanel.getComponent(0) instanceof TWizardPage) { + ((TWizardPage) pagePanel.getComponent(0)).pageDeleted(this); + } + } + pagePanel.removeAll(); + pagePanel.add(page, BorderLayout.CENTER); + page.pageAdded(this); + pack(); + setTitle(page.getHeading()); + setVisible(true); + } + + public TWizardPage getPage() { + return (TWizardPage) pagePanel.getComponent(0); + } + + public void setPreviousEnabled(boolean previousEnabled) { + previousAction.setEnabled(previousEnabled); + } + + public void setPreviousName(String previousName) { + previousAction.putValue(Action.NAME, previousName); + } + + public void setNextEnabled(boolean nextEnabled) { + nextAction.setEnabled(nextEnabled); + } + + public void setNextName(String nextName) { + nextAction.putValue(Action.NAME, nextName); + } + + public void setQuitEnabled(boolean quitEnabled) { + quitAction.setEnabled(quitEnabled); + } + + public void setQuitName(String quitName) { + quitAction.putValue(Action.NAME, quitName); + } + + protected void actionPrevious() { + fireWizardPreviousPressed(); + } + + protected void actionNext() { + fireWizardNextPressed(); + } + + protected void actionQuit() { + fireWizardQuitPressed(); + } + + // + // INTERFACE WindowListener + // + + /** + * {@inheritDoc} + */ + public void windowOpened(WindowEvent e) { + } + + /** + * {@inheritDoc} + */ + public void windowClosing(WindowEvent e) { + fireWizardQuitPressed(); + } + + /** + * {@inheritDoc} + */ + public void windowClosed(WindowEvent e) { + } + + /** + * {@inheritDoc} + */ + public void windowIconified(WindowEvent e) { + } + + /** + * {@inheritDoc} + */ + public void windowDeiconified(WindowEvent e) { + } + + /** + * {@inheritDoc} + */ + public void windowActivated(WindowEvent e) { + } + + /** + * {@inheritDoc} + */ + public void windowDeactivated(WindowEvent e) { + } + +} diff --git a/src/main/java/de/todesbaum/util/swing/TWizardPage.java b/src/main/java/de/todesbaum/util/swing/TWizardPage.java new file mode 100644 index 0000000..a10b475 --- /dev/null +++ b/src/main/java/de/todesbaum/util/swing/TWizardPage.java @@ -0,0 +1,83 @@ +/* + * jSite - TWizardPage.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.swing; + +import javax.swing.JPanel; + +/** + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public class TWizardPage extends JPanel { + + protected final TWizard wizard; + protected String heading; + protected String description; + + public TWizardPage(final TWizard wizard) { + this.wizard = wizard; + } + + public TWizardPage(final TWizard wizard, String heading) { + this.wizard = wizard; + this.heading = heading; + } + + public TWizardPage(final TWizard wizard, String heading, String description) { + this(wizard, heading); + this.description = description; + } + + /** + * @return Returns the description. + */ + public String getDescription() { + return description; + } + + /** + * @param description + * The description to set. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * @return Returns the heading. + */ + public String getHeading() { + return heading; + } + + /** + * @param heading + * The heading to set. + */ + public void setHeading(String heading) { + this.heading = heading; + } + + public void pageAdded(TWizard wizard) { + } + + public void pageDeleted(TWizard wizard) { + } + +} diff --git a/src/main/java/de/todesbaum/util/swing/WizardListener.java b/src/main/java/de/todesbaum/util/swing/WizardListener.java new file mode 100644 index 0000000..987075d --- /dev/null +++ b/src/main/java/de/todesbaum/util/swing/WizardListener.java @@ -0,0 +1,34 @@ +/* + * jSite - WizardListener.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.swing; + +import java.util.EventListener; + + +/** + * @author David Roden <droden@gmail.com> + * @version $Id$ + */ +public interface WizardListener extends EventListener { + + public void wizardNextPressed(TWizard wizard); + public void wizardPreviousPressed(TWizard wizard); + public void wizardQuitPressed(TWizard wizard); + +} diff --git a/src/main/java/de/todesbaum/util/xml/SimpleXML.java b/src/main/java/de/todesbaum/util/xml/SimpleXML.java new file mode 100644 index 0000000..8351581 --- /dev/null +++ b/src/main/java/de/todesbaum/util/xml/SimpleXML.java @@ -0,0 +1,338 @@ +/* + * jSite - SimpleXML.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.xml; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +/** + * SimpleXML is a helper class to construct XML trees in a fast and simple way. + * Construct a new XML tree by calling {@link #SimpleXML(String)} and append new + * nodes by calling {@link #append(String)}. + * + * @author David Roden <droden@gmail.com> + * @version $Id:SimpleXML.java 221 2006-03-06 14:46:49Z bombe $ + */ +public class SimpleXML { + + /** + * A {@link List} containing all child nodes of this node. + */ + private List children = new ArrayList(); + + /** + * The name of this node. + */ + private String name = null; + + /** + * The value of this node. + */ + private String value = null; + + /** + * Constructs a new XML node without a name. + */ + public SimpleXML() { + super(); + } + + /** + * Constructs a new XML node with the specified name. + * + * @param name + * The name of the new node + */ + public SimpleXML(String name) { + this.name = name; + } + + /** + * Returns the child node of this node with the specified name. If there are + * several child nodes with the specified name only the first node is + * returned. + * + * @param nodeName + * The name of the child node + * @return The child node, or null if there is no child node + * with the specified name + */ + public SimpleXML getNode(String nodeName) { + for (int index = 0, count = children.size(); index < count; index++) { + if (children.get(index).name.equals(nodeName)) { + return children.get(index); + } + } + return null; + } + + /** + * Returns the child node that is specified by the names. The first element + * of nodeNames is the name of the child node of this node, the + * second element of nodeNames is the name of a child node's + * child node, and so on. By using this method you can descend into an XML + * tree pretty fast. + * + *

+	 *
+	 * SimpleXML deepNode = topNode.getNodes(new String[] { "person", "address", "number" });
+	 * 
+ * + * @param nodeNames + * @return A node that is a deep child of this node, or null if + * the specified node does not eixst + */ + public SimpleXML getNode(String[] nodeNames) { + SimpleXML node = this; + for (String nodeName : nodeNames) { + node = node.getNode(nodeName); + } + return node; + } + + /** + * Returns all child nodes of this node. + * + * @return All child nodes of this node + */ + public SimpleXML[] getNodes() { + return getNodes(null); + } + + /** + * Returns all child nodes of this node with the specified name. If there + * are no child nodes with the specified name an empty array is returned. + * + * @param nodeName + * The name of the nodes to retrieve, or null to + * retrieve all nodes + * @return All child nodes with the specified name + */ + public SimpleXML[] getNodes(String nodeName) { + List resultList = new ArrayList(); + for (SimpleXML child : children) { + if ((nodeName == null) || child.name.equals(nodeName)) { + resultList.add(child); + } + } + return resultList.toArray(new SimpleXML[resultList.size()]); + } + + /** + * Appends a new XML node with the specified name and returns the new node. + * With this method you can create deep structures very fast. + * + *
+	 *
+	 * SimpleXML mouseNode = topNode.append("computer").append("bus").append("usb").append("mouse");
+	 * 
+ * + * @param nodeName + * The name of the node to append as a child to this node + * @return The new node + */ + public SimpleXML append(String nodeName) { + return append(new SimpleXML(nodeName)); + } + + /** + * Appends a new XML node with the specified name and value and returns the + * new node. + * + * @param nodeName + * The name of the node to append + * @param nodeValue + * The value of the node to append + * @return The newly appended node + */ + public SimpleXML append(String nodeName, String nodeValue) { + return append(nodeName).setValue(nodeValue); + } + + /** + * Appends the node with all its child nodes to this node and returns the + * child node. + * + * @param newChild + * The node to append as a child + * @return The child node that was appended + */ + public SimpleXML append(SimpleXML newChild) { + children.add(newChild); + return newChild; + } + + public void remove(SimpleXML child) { + children.remove(child); + } + + public void remove(String childName) { + SimpleXML child = getNode(childName); + if (child != null) { + remove(child); + } + } + + public void replace(String childName, String value) { + remove(childName); + append(childName, value); + } + + public void replace(SimpleXML childNode) { + remove(childNode.getName()); + append(childNode); + } + + public void removeAll() { + children.clear(); + } + + /** + * Sets the value of this node. + * + * @param nodeValue + * The new value of this node + * @return This node + */ + public SimpleXML setValue(String nodeValue) { + value = nodeValue; + return this; + } + + /** + * Returns the name of this node. + * + * @return The name of this node + */ + public String getName() { + return name; + } + + /** + * Returns the value of this node. + * + * @return The value of this node + */ + public String getValue() { + return value; + } + + /** + * Returns the value of this node. If the node does not have a value, the + * given default value is returned. + * + *@param defaultValue + * The default value to return if the node does not have a value + * @return The value of this node + */ + public String getValue(String defaultValue) { + return (value == null) ? defaultValue : value; + } + + /** + * Creates a {@link Document} from this node and all its child nodes. + * + * @return The {@link Document} created from this node + */ + public Document getDocument() { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + try { + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.newDocument(); + Element rootElement = document.createElement(name); + document.appendChild(rootElement); + addChildren(rootElement); + return document; + } catch (ParserConfigurationException e) { + } + return null; + } + + /** + * Appends all children of this node to the specified {@link Element}. If a + * node has a value that is not null the value is appended as a + * text node. + * + * @param rootElement + * The element to attach this node's children to + */ + private void addChildren(Element rootElement) { + for (SimpleXML child : children) { + Element childElement = rootElement.getOwnerDocument().createElement(child.name); + rootElement.appendChild(childElement); + if (child.value != null) { + Text childText = rootElement.getOwnerDocument().createTextNode(child.value); + childElement.appendChild(childText); + } else { + child.addChildren(childElement); + } + } + } + + /** + * Creates a SimpleXML node from the specified {@link Document}. The + * SimpleXML node of the document's top-level node is returned. + * + * @param document + * The {@link Document} to create a SimpleXML node from + * @return The SimpleXML node created from the document's top-level node + */ + public static SimpleXML fromDocument(Document document) { + SimpleXML xmlDocument = new SimpleXML(document.getFirstChild().getNodeName()); + document.normalizeDocument(); + return addDocumentChildren(xmlDocument, document.getFirstChild()); + } + + /** + * Appends the child nodes of the specified {@link Document} to this node. + * Text nodes are converted into a node's value. + * + * @param xmlDocument + * The SimpleXML node to append the child nodes to + * @param document + * The document whose child nodes to append + * @return The SimpleXML node the child nodes were appended to + */ + private static SimpleXML addDocumentChildren(SimpleXML xmlDocument, Node document) { + NodeList childNodes = document.getChildNodes(); + for (int childIndex = 0, childCount = childNodes.getLength(); childIndex < childCount; childIndex++) { + Node childNode = childNodes.item(childIndex); + if ((childNode.getChildNodes().getLength() == 1) && (childNode.getFirstChild().getNodeName().equals("#text"))) { + xmlDocument.append(childNode.getNodeName(), childNode.getFirstChild().getNodeValue()); + } else { + if (!childNode.getNodeName().equals("#text") || (childNode.getChildNodes().getLength() != 0)) { + SimpleXML newXML = xmlDocument.append(childNode.getNodeName()); + addDocumentChildren(newXML, childNode); + } + } + } + return xmlDocument; + } + +} diff --git a/src/main/java/de/todesbaum/util/xml/XML.java b/src/main/java/de/todesbaum/util/xml/XML.java new file mode 100644 index 0000000..8b6b259 --- /dev/null +++ b/src/main/java/de/todesbaum/util/xml/XML.java @@ -0,0 +1,179 @@ +/* + * jSite - XML.java - Copyright © 2006–2012 David Roden + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package de.todesbaum.util.xml; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.w3c.dom.Document; + + +/** + * Contains method to transform DOM XML trees to byte arrays and vice versa. + * + * @author David Roden <droden@gmail.com> + * @version $Id:XML.java 221 2006-03-06 14:46:49Z bombe $ + */ +public class XML { + + /** Cached document builder factory. */ + private static DocumentBuilderFactory documentBuilderFactory = null; + + /** Cached document builder. */ + private static DocumentBuilder documentBuilder = null; + + /** Cached transformer factory. */ + private static TransformerFactory transformerFactory = null; + + /** Does nothing. */ + private XML() { + } + + /** + * Returns a document builder factory. If possible the cached instance will be returned. + * + * @return A document builder factory + */ + private static DocumentBuilderFactory getDocumentBuilderFactory() { + if (documentBuilderFactory != null) { + return documentBuilderFactory; + } + documentBuilderFactory = DocumentBuilderFactory.newInstance(); + return documentBuilderFactory; + } + + /** + * Returns a document builder. If possible the cached instance will be returned. + * + * @return A document builder + */ + private static DocumentBuilder getDocumentBuilder() { + if (documentBuilder != null) { + return documentBuilder; + } + try { + documentBuilder = getDocumentBuilderFactory().newDocumentBuilder(); + } catch (ParserConfigurationException e) { + } + return documentBuilder; + } + + /** + * Returns a transformer factory. If possible the cached instance will be returned. + * + * @return A transformer factory + */ + private static TransformerFactory getTransformerFactory() { + if (transformerFactory != null) { + return transformerFactory; + } + transformerFactory = TransformerFactory.newInstance(); + return transformerFactory; + } + + /** + * Creates a new XML document. + * + * @return A new XML document + */ + public static Document createDocument() { + return getDocumentBuilder().newDocument(); + } + + /** + * Transforms the DOM XML document into a byte array. + * + * @param document + * The document to transform + * @return The byte array containing the XML representation + */ + public static byte[] transformToByteArray(Document document) { + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + OutputStreamWriter converter = new OutputStreamWriter(byteOutput, Charset.forName("UTF-8")); + Result transformResult = new StreamResult(converter); + Source documentSource = new DOMSource(document); + try { + Transformer transformer = getTransformerFactory().newTransformer(); + transformer.transform(documentSource, transformResult); + byteOutput.close(); + return byteOutput.toByteArray(); + } catch (IOException ioe1) { + } catch (TransformerConfigurationException tce1) { + } catch (TransformerException te1) { + } finally { + try { + byteOutput.close(); + } catch (IOException ioe1) { + } + } + return null; + } + + /** + * Transforms the byte array into a DOM XML document. + * + * @param data + * The byte array to parse + * @return The DOM XML document + */ + public static Document transformToDocument(byte[] data) { + ByteArrayInputStream byteInput = new ByteArrayInputStream(data); + InputStreamReader converter = new InputStreamReader(byteInput, Charset.forName("UTF-8")); + Source xmlSource = new StreamSource(converter); + Result xmlResult = new DOMResult(); + try { + Transformer transformer = getTransformerFactory().newTransformer(); + transformer.transform(xmlSource, xmlResult); + return (Document) ((DOMResult) xmlResult).getNode(); + } catch (TransformerConfigurationException tce1) { + } catch (TransformerException te1) { + } finally { + if (byteInput != null) + try { + byteInput.close(); + } catch (IOException ioe1) { + } + if (converter != null) + try { + converter.close(); + } catch (IOException ioe1) { + } + } + return null; + } + +} diff --git a/src/main/resources/de/todesbaum/jsite/i18n/jSite.properties b/src/main/resources/de/todesbaum/jsite/i18n/jSite.properties new file mode 100644 index 0000000..cd80c67 --- /dev/null +++ b/src/main/resources/de/todesbaum/jsite/i18n/jSite.properties @@ -0,0 +1,196 @@ +# +# jSite - jSite.properties - Copyright © 2006–2012 David Roden +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# English language file by David Roden + +# Attention, translators! Most of the strings here are used directly. +# However, some of them are parsed by MessageFormat +# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html) +# and thus have to adhere to some rules (check the URL above). This is the +# case when a line contains placeholders like {0} or {0,number}! In these +# lines single quotes (ASCII 39) needs to be escaped by entering them twice, +# otherwise the placeholder will not be replaced! + +jsite.general.ok=OK +jsite.general.cancel=Cancel + +jsite.wizard.previous=Previous +jsite.wizard.next=Next +jsite.wizard.quit=Quit + +jsite.quit.question=Do you really want to quit? +jsite.quit.question.title=Really quit? +jsite.quit.overwrite-configuration=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 +jsite.menu.language.en=English +jsite.menu.language.de=Deutsch +jsite.menu.language.fr=Fran\u00e7ais +jsite.menu.language.it=Italiano +jsite.menu.language.pl=Polski +jsite.menu.nodes=Nodes +jsite.menu.nodes.manage-nodes=Manage nodes +jsite.menu.options=Options +jsite.menu.options.preferences=Preferences +jsite.menu.help=Help +jsite.menu.help.check-for-updates=Check for Updates +jsite.menu.help.about=About + +jsite.about.message=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. +jsite.node-manager.node-information=Node Information +jsite.node-manager.add-node=Add Node +jsite.node-manager.new-node=New Node +jsite.node-manager.delete-node=Delete Node +jsite.node-manager.delete-node.warning=Confirm node deletion

Really delete this node? +jsite.node-manager.name=Name +jsite.node-manager.hostname=Hostname +jsite.node-manager.port=Port + +jsite.preferences.heading=Preferences +jsite.preferences.description=Use this page to manage some global settings. +jsite.preferences.temp-directory=Directory for temporary files +jsite.preferences.temp-directory.default=Default (chosen by system) +jsite.preferences.temp-directory.custom=Custom +jsite.preferences.temp-directory.choose=Choose +jsite.preferences.temp-directory.choose.approve=Choose +jsite.preferences.config-directory=Location of configuration file +jsite.preferences.config-directory.jar=Next to the JAR file +jsite.preferences.config-directory.home=Home directory +jsite.preferences.config-directory.custom=Custom directory +jsite.preferences.insert-options=Insert options +jsite.preferences.insert-options.use-early-encode=Generate final URI early +jsite.preferences.insert-options.priority=Priority +jsite.preferences.insert-options.manifest-putter=Manifest Putter + +jsite.insert.heading=Project insert +jsite.insert.description=Please wait while the project is being inserted. +jsite.insert.project-information=Project information +jsite.insert.request-uri=Freesite +jsite.insert.start-time=Start time +jsite.insert.starting=Starting\u2026 +jsite.insert.done=Done. +jsite.insert.done.title=Insert done +jsite.insert.insert-aborted=The insert was aborted. +jsite.insert.insert-aborted.title=Insert Aborted +jsite.insert.progress=Progress +jsite.insert.k-per-s=KB/s +jsite.insert.insert-failed=Insert failed

The insert of the project failed.
Some files could not be inserted. +jsite.insert.insert-failed-with-cause=Insert failed

The insert of the project failed.
Some files could not be inserted.
The following error occured:

{0} +jsite.insert.insert-failed.title=Insert Failed +jsite.insert.inserted=Project inserted

Your project was inserted successfully. +jsite.insert.okay-copy-uri=Copy URI to Clipboard +jsite.insert.reinserted-edition=Edition Reinserted

The edition you are just inserting
has already been inserted before. +jsite.insert.reinserted-edition.title=Edition Reinserted + +jsite.file-scanner.can-not-read-directory=Can not read directory + +jsite.project.heading=Select a Project +jsite.project.description=Select a project to process from the list below, or create a new project. +jsite.project.action.browse=Browse +jsite.project.action.browse.choose=Choose +jsite.project.action.browse.tooltip=Browse for directory +jsite.project.action.add-project=Add project +jsite.project.action.add-project.tooltip=Add a new project +jsite.project.new-project.name=New Project +jsite.project.action.delete-project=Delete project +jsite.project.action.delete-project.tooltip=Delete a project +jsite.project.action.delete-project.confirm=Confirm deletion

The project \u201c{0}\u201d will be deleted!
Do you want to continue? +jsite.project.action.clone-project=Clone project +jsite.project.action.clone-project.copy=Copy of {0} +jsite.project.action.clone-project.tooltip=Clone the selected project +jsite.project.action.copy-uri=Copy URI to Clipboard +jsite.project.action.copy-uri.tooltip=Copies the URI of the project to the clipboard +jsite.project.action.manage-keys=Manage Keys +jsite.project.action.manage-keys.tooltip=Manages the keys of this project +jsite.project.action.reset-edition=Reset Edition +jsite.project.action.reset-edition.tooltip=Resets the edition number of the project +jsite.project.project.information=Project Information +jsite.project.project.name=Name +jsite.project.project.description=Description +jsite.project.project.local-path=Local path +jsite.project.project.address=Address +jsite.project.project.path=Freesite Path +jsite.project.project.edition=Edition +jsite.project.project.uri=URI +jsite.project.keygen.io-error=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!
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! + +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.ignore-hidden-files=Ignore hidden files +jsite.project-files.ignore-hidden-files.tooltip=When selected, hidden files are not inserted +jsite.project-files.file-options=File Options +jsite.project-files.default=Default file +jsite.project-files.default.tooltip=Specify that this file is the project\u2019s index file +jsite.project-files.insert=Insert +jsite.project-files.insert.tooltip=Uncheck if you do not want to insert this file +jsite.project-files.force-insert=Force insert +jsite.project-files.force-insert.tooltip=Forces the insert of this file even it is not modified +jsite.project-files.insert-redirect=Redirect +jsite.project-files.insert-redirect.tooltip=Check if you want to insert a redirect for this file +jsite.project-files.custom-key=Custom key +jsite.project-files.custom-key.tooltip=The externally created key for the file +jsite.project-files.rename=Rename +jsite.project-files.rename.tooltip=Renames the file in the uploaded site +jsite.project-files.mime-type=MIME type +jsite.project-files.mime-type.tooltip=Select the correct MIME type here if the detection failed +jsite.project-files.container=Container +jsite.project-files.container.tooltip=Selects a container for the current file +jsite.project-files.scan-error=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}) +jsite.update-checker.latest-version.title=Update Check +jsite.update-checker.latest-version.newer.message=You are running version {0} but a newer
version ({1}) has been found! +jsite.update-checker.latest-version.older.message=You are running version {0} but the
latest version seems to be {1}. +jsite.update-checker.latest-version.okay.message=You are currently running version {0}
which is the latest version. + +jsite.key-dialog.title=Manage Project Keys +jsite.key-dialog.button.ok.tooltip=Accepts the changes +jsite.key-dialog.button.cancel.tooltip=Discards the changes +jsite.key-dialog.button.generate=Regenerate Keys +jsite.key-dialog.button.generate.tooltip=Create a new key pair +jsite.key-dialog.label.keys=Keys +jsite.key-dialog.label.private-key=Private Key +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.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.warning.site-larger-than-2-mib=Site is larger than 2 MiB!

Your site contains more than 2 megabytes of data.
Due to bugs in Freenet it will probably not load correctly.
Try to reduce the size of your site, or continue at your own peril. + +jsite.error.no-node-selected=No node selected

Please select a node from the menu! +jsite.error.no-node-running=Node is not running

You can not insert a project if your node is not running.
Please start your node and try again. +jsite.error.no-local-path=No local path

You did not specify a local path for the files to insert.
It is not possible to continue without one. +jsite.error.no-path=No freesite path

You did not specify a freesite path.
It is not possible to continue without one. +jsite.error.index-missing=Your default file is missing

A default file was previously specified but it
does not exist anymore! Please select
a new default file in the list of files. +jsite.error.index-not-inserted=Default file not inserted

You have chosen not to insert the default file!
You need to either choose to insert it or select
a different default file! +jsite.error.no-custom-key=No custom key for file

You specified not to insert {0}
but failed to enter a key to redirect to! +jsite.error.no-files-to-insert=No files to insert

You do not have any files selected for insertion!
Please select at least one file to insert. +jsite.error.duplicate-file=Duplicate file

The file {0} is inserted twice!
Please check your filenames and redirects. diff --git a/src/main/resources/de/todesbaum/jsite/i18n/jSite_de.properties b/src/main/resources/de/todesbaum/jsite/i18n/jSite_de.properties new file mode 100644 index 0000000..8641d14 --- /dev/null +++ b/src/main/resources/de/todesbaum/jsite/i18n/jSite_de.properties @@ -0,0 +1,196 @@ +# +# jSite - jSite_de.properties - Copyright © 2006–2012 David Roden +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# German language file by David Roden + +# Attention, translators! Most of the strings here are used directly. +# However, some of them are parsed by MessageFormat +# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html) +# and thus have to adhere to some rules (check the URL above). This is the +# case when a line contains placeholders like {0} or {0,number}! In these +# lines single quotes (ASCII 39) needs to be escaped by entering them twice, +# otherwise the placeholder will not be replaced! + +jsite.general.ok=OK +jsite.general.cancel=Abbrechen + +jsite.wizard.previous=Zur\u00fcck +jsite.wizard.next=Vorw\u00e4rts +jsite.wizard.quit=Beenden + +jsite.quit.question=M\u00f6chten Sie jSite wirklich beenden? +jsite.quit.question.title=Wirklich beenden? +jsite.quit.overwrite-configuration=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 +jsite.menu.language.en=English +jsite.menu.language.de=Deutsch +jsite.menu.language.fr=Fran\u00e7ais +jsite.menu.language.it=Italiano +jsite.menu.language.pl=Polski +jsite.menu.nodes=Nodes +jsite.menu.nodes.manage-nodes=Nodes verwalten +jsite.menu.options=Optionen +jsite.menu.options.preferences=Einstellungen +jsite.menu.help=Hilfe +jsite.menu.help.check-for-updates=Auf Updates pr\u00fcfen +jsite.menu.help.about=\u00dcber + +jsite.about.message=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. +jsite.node-manager.node-information=Nodeinformation +jsite.node-manager.add-node=Node hinzuf\u00fcgen +jsite.node-manager.new-node=Neuer Node +jsite.node-manager.delete-node=Node l\u00f6schen +jsite.node-manager.delete-node.warning=Nodel\u00f6schung best\u00e4tigen

Wollen Sie diesen Node wirklich l\u00f6schen? +jsite.node-manager.name=Name +jsite.node-manager.hostname=Hostname +jsite.node-manager.port=Port + +jsite.preferences.heading=Einstellungen +jsite.preferences.description=Auf dieser Seite k\u00f6nnen einige globale Einstellungen vorgenommen werden. +jsite.preferences.temp-directory=Verzeichnis f\u00fcr tempor\u00e4re Dateien +jsite.preferences.temp-directory.default=Standard (vom System bestimmt) +jsite.preferences.temp-directory.custom=Eigenes +jsite.preferences.temp-directory.choose=Ausw\u00e4hlen +jsite.preferences.temp-directory.choose.approve=Ausw\u00e4hlen +jsite.preferences.config-directory=Lage der Konfigurationsdatei +jsite.preferences.config-directory.jar=Neben der JAR-Datei +jsite.preferences.config-directory.home=Benutzerverzeichnis +jsite.preferences.config-directory.custom=Angegebenes Verzeichnis +jsite.preferences.insert-options=Einf\u00fcgeoptionen +jsite.preferences.insert-options.use-early-encode=Endg\u00fcltige URI fr\u00fcher berechnen +jsite.preferences.insert-options.priority=Priorit\u00e4t +jsite.preferences.insert-options.manifest-putter=Manifesterstellung + +jsite.insert.heading=Projekt einf\u00fcgen +jsite.insert.description=Bitte warten Sie, w\u00e4hrend das Projekt eingef\u00fcgt wird. +jsite.insert.project-information=Projektinformationen +jsite.insert.request-uri=Freesite +jsite.insert.start-time=Beginn +jsite.insert.starting=Beginne\u2026 +jsite.insert.done=Fertig. +jsite.insert.done.title=Einf\u00fcgen abgeschlossen +jsite.insert.insert-aborted=Das Einf\u00fcgen wurde abgebrochen. +jsite.insert.insert-aborted.title=Einf\u00fcgen abgebrochen +jsite.insert.progress=Fortschritt +jsite.insert.k-per-s=KB/s +jsite.insert.insert-failed=Einf\u00fcgen fehlgeschlagen

Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da
einige Dateien nicht eingef\u00fcgt werden konnten. +jsite.insert.insert-failed-with-cause=Einf\u00fcgen fehlgeschlagen

Das Einf\u00fcgen des Projektes ist fehlgeschlagen, da
einige Dateien nicht eingef\u00fcgt werden konnten.
Folgender Fehler trat auf:

{0} +jsite.insert.insert-failed.title=Einf\u00fcgen fehlgeschlagen +jsite.insert.inserted=Projekt eingef\u00fcgt

Ihr Projekt wurde erfolgreich eingef\u00fcgt. +jsite.insert.okay-copy-uri=URI kopieren +jsite.insert.reinserted-edition=Edition bereits eingef\u00fcgt

Die Edition, die gerade eingef\u00fcgt wird,
ist schon einmal eingef\u00fcgt worden. +jsite.insert.reinserted-edition.title=Edition bereits eingef\u00fcgt + +jsite.file-scanner.can-not-read-directory=Kann Verzeichnis nicht lesen + +jsite.project.heading=Projekt ausw\u00e4hlen +jsite.project.description=W\u00e4hlen Sie das Projekt aus, welches sie einf\u00fcgen m\u00f6chten, oder erstellen Sie ein neues Projekt. +jsite.project.action.browse=Durchsuchen +jsite.project.action.browse.choose=Ausw\u00e4hlen +jsite.project.action.browse.tooltip=Lokalen Pfad f\u00fcr Projekt ausw\u00e4hlen +jsite.project.action.add-project=Projekt erstellen +jsite.project.action.add-project.tooltip=Ein neues Projekt erstellen +jsite.project.new-project.name=Neues Projekt +jsite.project.action.delete-project=Projekt l\u00f6schen +jsite.project.action.delete-project.tooltip=Ein Projekt l\u00f6schen +jsite.project.action.delete-project.confirm=L\u00f6schung best\u00e4tigen

Das Projekt \u201e{0}\u201c wird gel\u00f6scht!
M\u00f6chten Sie fortfahren? +jsite.project.action.clone-project=Projekt duplizieren +jsite.project.action.clone-project.copy=Kopie von {0} +jsite.project.action.clone-project.tooltip=Das ausgew\u00e4hlte Projekt duplizieren +jsite.project.action.copy-uri=URI kopieren +jsite.project.action.copy-uri.tooltip=Kopiert die URI des ausgew\u00e4hlten Projektes in die Zwischenablage +jsite.project.action.manage-keys=Schl\u00fcsselverwaltung +jsite.project.action.manage-keys.tooltip=Verwaltet die Schl\u00fcssel des Projekts +jsite.project.action.reset-edition=Edition zur\u00fccksetzen +jsite.project.action.reset-edition.tooltip=Setzt die Editionsnummer des Projekts zur\u00fcck +jsite.project.project.information=Projektinformation +jsite.project.project.name=Name +jsite.project.project.description=Beschreibung +jsite.project.project.local-path=Lokaler Pfad +jsite.project.project.address=Adresse +jsite.project.project.path=Seitenpfad +jsite.project.project.edition=Edition +jsite.project.project.uri=Anfrage-URI +jsite.project.keygen.io-error=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! 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! + +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.ignore-hidden-files=Versteckte Dateien ignorieren +jsite.project-files.ignore-hidden-files.tooltip=Verhindert, dass versteckte Dateien hochgeladen werden +jsite.project-files.file-options=Dateioptionen +jsite.project-files.default=Index-Datei +jsite.project-files.default.tooltip=Lege Index-Datei f\u00fcr Projekt fest +jsite.project-files.insert=Einf\u00fcgen +jsite.project-files.insert.tooltip=jSite f\u00fcgt diese Datei ein +jsite.project-files.force-insert=Einf\u00fcgen erzwingen +jsite.project-files.force-insert.tooltip=F\u00fcgt diese Datei ein, auch wenn sie nicht modifiziert wurde +jsite.project-files.insert-redirect=Umleitung +jsite.project-files.insert-redirect.tooltip=F\u00fcgt eine Umleitung ein +jsite.project-files.custom-key=Extern erstellter Schl\u00fcssel +jsite.project-files.custom-key.tooltip=Der extern erstellte Schl\u00fcssel f\u00fcr diese Datei +jsite.project-files.rename=Umbenennen +jsite.project-files.rename.tooltip=Benennt die Datei in der eingef\u00fcgten Seite um +jsite.project-files.mime-type=MIME-Typ +jsite.project-files.mime-type.tooltip=Den richtigen MIME-Typ hier ausw\u00e4hlen, wenn die automatische Erkennenung falsch ist +jsite.project-files.container=Container +jsite.project-files.container.tooltip=W\u00e4hlt einen Container f\u00fcr diese Datei aus +jsite.project-files.scan-error=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}) +jsite.update-checker.latest-version.title=Update\u00fcberpr\u00fcfung +jsite.update-checker.latest-version.newer.message=Es l\u00e4uft momentan Version {0}, aber eine
neue Version ({1}) wurde bereits gefunden! +jsite.update-checker.latest-version.older.message=Es l\u00e4uft momentan Version {0}, aber die
aktuelle Version ist erst {1}. +jsite.update-checker.latest-version.okay.message=Es l\u00e4uft momentan Version {0},
und diese Version ist aktuell. + +jsite.key-dialog.title=Projektschl\u00fcsselverwaltung +jsite.key-dialog.button.ok.tooltip=\u00c4nderungen akzeptieren +jsite.key-dialog.button.cancel.tooltip=\u00c4nderungen verwerfen +jsite.key-dialog.button.generate=Schl\u00fcssel neu generieren +jsite.key-dialog.button.generate.tooltip=Generiert ein neues Schl\u00fcsselpaar +jsite.key-dialog.label.keys=Schl\u00fcssel +jsite.key-dialog.label.private-key=Privater Schl\u00fcssel +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.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.warning.site-larger-than-2-mib=Ihr Projekt ist gr\u00f6\u00dfer als 2 Megabyte!

Ihr Projekt enth\u00e4lt mehr als 2 Megabyte an Daten. Aufgrund
eines Fehlers in Freenet wird die Seite wahrscheinlich nicht
korrekt angezeigt werden. Bitte reduzieren Sie die Gr\u00f6\u00dfe
Ihres Projektes, oder fahren Sie auf eigene Gefahr fort. + +jsite.error.no-node-selected=Kein Node ausgew\u00e4hlt

Bitte w\u00e4hlen Sie einen Node aus dem Men\u00fc! +jsite.error.no-node-running=Der Node l\u00e4uft nicht

Sie k\u00f6nnen das Projekt nicht einf\u00fcgen, wenn
Ihr Node nicht l\u00e4uft. Bitte starten Sie Ihren Node
und probieren Sie es erneut. +jsite.error.no-local-path=Kein lokaler Pfad

Sie haben keinen lokalen Pfad f\u00fcr die einzuf\u00fcgenden Dateien angegeben.
Es ist nicht m\u00f6glich, ohne lokalen Pfad weiter zu machen. +jsite.error.no-path=Kein Freesite-Pfad

Sie haben keinen Pfad f\u00fcr die Freesite angegeben.
Es ist nicht m\u00f6glich, ohne einen Freesite-Pfad weiter zu machen. +jsite.error.index-missing=Index-Datei fehlt!

Sie haben eine Index-Datei f\u00fcr das Project gew\u00e4hlt,
aber diese Index-Datei existiert nicht mehr!
Bitte w\u00e4hlen Sie eine neue Index-Datei. +jsite.error.index-not-inserted=Index-Datei nicht eingef\u00fcgt

Die index-Datei ist nicht zum Einf\u00fcgen ausgew\u00e4hlt!
Sie m\u00fcssen entweder w\u00e4hlen, die Index-Datei einzuf\u00fcgen,
oder Sie m\u00fcssen eine andere Index-Datei ausw\u00e4hlen! +jsite.error.no-custom-key=Kein externer Schl\u00fcssel

Sie haben angegeben, dass die Datei {0}
nicht eingef\u00fcgt werden soll. Allerdings haben Sie
keinen extern erstellten Schl\u00fcssel angegeben. +jsite.error.no-files-to-insert=Keine Dateien einzuf\u00fcgen

Es sind keine Dateien zum Einf\u00fcgen ausgew\u00e4hlt! Bitte
w\u00e4hlen Sie mindestens eine Datei zum Einf\u00fcgen aus! +jsite.error.duplicate-file=Doppelte Datei

Die Datei {0} wird zweimal eingef\u00fcgt!
Bitte \u00fcberpr\u00fcfen Sie Ihre Umleitungen und Dateinamen! diff --git a/src/main/resources/de/todesbaum/jsite/i18n/jSite_fr.properties b/src/main/resources/de/todesbaum/jsite/i18n/jSite_fr.properties new file mode 100644 index 0000000..6f1e8c6 --- /dev/null +++ b/src/main/resources/de/todesbaum/jsite/i18n/jSite_fr.properties @@ -0,0 +1,195 @@ +# +# jSite - jSite_fr.properties - Copyright © 2006–2012 David Roden +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# French language file by Florent Daigni\u00e8re , Julien Cornuwel , and Clement Vollet . + +# Attention, translators! Most of the strings here are used directly. +# However, some of them are parsed by MessageFormat +# (http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html) +# and thus have to adhere to some rules (check the URL above). This is the +# case when a line contains placeholders like {0} or {0,number}! In these +# lines single quotes (ASCII 39) needs to be escaped by entering them twice, +# otherwise the placeholder will not be replaced! + +jsite.general.ok=OK +jsite.general.cancel=Annuler + +jsite.wizard.previous=Pr\u00e9c\u00e9dent +jsite.wizard.next=Suivant +jsite.wizard.quit=Quitter + +jsite.quit.question=Voulez-vous r\u00e9ellement quitter? +jsite.quit.question.title=Souhaitez vous quitter? +jsite.quit.overwrite-configuration=Ecraser la configuration?

Un fichier de configuration \u00e9xiste d\u00e9j\u00e0:
{0}

Doit-il \u00eatre \u00e9cras\u00e9 ? +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 +jsite.menu.language.en=English +jsite.menu.language.de=Deutsch +jsite.menu.language.fr=Fran\u00e7ais +jsite.menu.language.it=Italiano +jsite.menu.language.pl=Polski +jsite.menu.nodes=Noeud +jsite.menu.nodes.manage-nodes=G\u00e9rer les noeuds +jsite.menu.options=Options +jsite.menu.options.preferences=Pr\u00e9f\u00e9rences +jsite.menu.help=Aide +jsite.menu.help.check-for-updates=Mises \u00e0 jour +jsite.menu.help.about=A propos de jSite + +jsite.about.message=jSite {0}

Copyright \u00a9 2006\u20132012 David Roden
Publi\u00e9 sous GNU General Public License + +jsite.node-manager.heading=Gestionnaire de noeud +jsite.node-manager.description=G\u00e9rez vos noeuds. +jsite.node-manager.node-information=Information sur le noeud +jsite.node-manager.add-node=Ajouter un noeud +jsite.node-manager.new-node=Nouveau noeud +jsite.node-manager.delete-node=Supprimer un noeud +jsite.node-manager.delete-node.warning=Confirmer la suppression

Supprimer r\u00e9ellement ce noeud? +jsite.node-manager.name=Nom +jsite.node-manager.hostname=Nom de machine +jsite.node-manager.port=Port + +jsite.preferences.heading=Pr\u00e9f\u00e9rences +jsite.preferences.description=Utilisez cette page pour g\u00e9rer quelques param\u00e8tres globaux. +jsite.preferences.temp-directory=R\u00e9pertoire des fichiers temporaires +jsite.preferences.temp-directory.default=D\u00e9faut (choix syst\u00e8mes) +jsite.preferences.temp-directory.custom=Personnalis\u00e9 +jsite.preferences.temp-directory.choose=Choisir +jsite.preferences.temp-directory.choose.approve=Choisir +jsite.preferences.config-directory=Chemin du fichier de configuration +jsite.preferences.config-directory.jar=Suivant, le fichier JAR +jsite.preferences.config-directory.home=Acceuil +jsite.preferences.config-directory.custom=R\u00e9pertoire personnel +jsite.preferences.insert-options=Options d'insertion +jsite.preferences.insert-options.use-early-encode=G\u00e9n\u00e9rer d'abord l'URI +jsite.preferences.insert-options.priority=Priorit\u00e9 +jsite.preferences.insert-options.manifest-putter=Ajout de Manifest + +jsite.insert.heading=Projet d'insertion +jsite.insert.description=Veuillez attendre durant l'insertion du projet. +jsite.insert.project-information=Information \u00e0 propos du projet. +jsite.insert.request-uri=Freesite +jsite.insert.start-time=Commenc\u00e9 \u00e0 +jsite.insert.starting=D\u00e9marrage\u2026 +jsite.insert.done=Termin\u00e9. +jsite.insert.done.title=Insertion effectu\u00e9e +jsite.insert.insert-aborted=L'insertion a \u00e9t\u00e9 annul\u00e9e. +jsite.insert.insert-aborted.title=Insertion Annul\u00e9e +jsite.insert.progress=Avancement +jsite.insert.k-per-s=Ko/s +jsite.insert.insert-failed=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\u00e9e +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. +jsite.insert.reinserted-edition.title=Edition r\u00e9ins\u00e9r\u00e9e + +jsite.file-scanner.can-not-read-directory=Impossible de lire le r\u00e9pertoire + +jsite.project.heading=S\u00e9lectionnez un projet +jsite.project.description=S\u00e9lectionnez un projet de la liste ou cr\u00e9ez en un nouveau. +jsite.project.action.browse=Naviguer +jsite.project.action.browse.choose=Choisir +jsite.project.action.browse.tooltip=Choisir un r\u00e9pertoire +jsite.project.action.add-project=Ajouter un projet +jsite.project.action.add-project.tooltip=Ajouter un projet +jsite.project.new-project.name=Nouveau projet +jsite.project.action.delete-project=D\u00e9truire le projet +jsite.project.action.delete-project.tooltip=D\u00e9truire le projet +jsite.project.action.delete-project.confirm=Confirmez la suppression

Le projet \u201c{0}\u201d va \u00eatre d\u00e9truit!
Voulez vous poursuivre? +jsite.project.action.clone-project=Cloner le projet +jsite.project.action.clone-project.copy=Copie de {0} +jsite.project.action.clone-project.tooltip=Cloner le projet s\u00e9lectionn\u00e9 +jsite.project.action.copy-uri=Copier l'URI dans le presse-papier +jsite.project.action.copy-uri.tooltip=Copie l'URI du projet dans le presse-papier +jsite.project.action.generate-new-key=G\u00e9n\u00e9rer une nouvelle cl\u00e9 +jsite.project.action.generate-new-key.tooltip=Cr\u00e9e une nouvelle cl\u00e9 pour ce projet +jsite.project.action.reset-edition=Remettre \u00e0 z\u00e9ro l'\u00e9dition +jsite.project.action.reset-edition.tooltip=Remettre \u00e0 z\u00e9ro l'\u00e9dition du projet +jsite.project.project.information=Informations concernant le projet +jsite.project.project.name=Nom +jsite.project.project.description=Description +jsite.project.project.local-path=Chemin local +jsite.project.project.address=Adresse +jsite.project.project.path=Chemin du freesite +jsite.project.project.edition=Edition +jsite.project.project.uri=URI +jsite.project.keygen.io-error=Erreur de communication avec le noeud

La communication avec le noeud \u00e0 \u00e9chou\u00e9e
Erreur:

{0}

Assurez vous que les informations saisies dans la page de configuration sont correctes. +jsite.project.warning.generate-new-key=G\u00e9n\u00e9rer une nouvelle cl\u00e9 ?

Si vous g\u00e9n\u00e9rez une nouvelle cl\u00e9, votre site sera publi\u00e9
avec cette nouvelle cl\u00e9. La confiance que les autres
utilisateurs pla\u00e7aient dans l'ancienne cl\u00e9 sera perdue ! +jsite.project.warning.reset-edition=Remettre \u00e0 z\u00e9ro l'\u00e9dition ?

Remettre \u00e0 z\u00e9ro l'\u00e9dition peut faire \u00e9chouer l'insertion
ou poser des probl\u00e8mes si vous n'avez pas chang\u00e9
le chemin ou les cl\u00e9s du projet ! +jsite.project.warning.use-clipboard-now=URI copi\u00e9e

Veuillez noter qu'il est possible qu'en quittant jSite
maintenant le presse-papiers soit vid\u00e9. Merci d'utiliser
l'URI copi\u00e9e imm\u00e9diatement dans une autre fen\u00eatre ! + +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.ignore-hidden-files=Ignorer les fichiers cach\u00e9s +jsite.project-files.ignore-hidden-files.tooltip=Si s\u00e9lectionn\u00e9, les fichiers cach\u00e9s ne sont pas ins\u00e9r\u00e9s +jsite.project-files.file-options=Option des fichiers +jsite.project-files.default=Fichier par d\u00e9faut +jsite.project-files.default.tooltip=Est-ce l'index? +jsite.project-files.insert=Ins\u00e9rer +jsite.project-files.insert.tooltip=D\u00e9cochez si vous ne voulez pas ins\u00e9rer ce fichier +jsite.project-files.force-insert=Forcer l'insertion +jsite.project-files.force-insert.tooltip=Forcer l'insertion de ce fichier tant qu'il n'est pas modifi\u00e9 +jsite.project-files.insert-redirect=Redirection +jsite.project-files.insert-redirect.tooltip=Cochez si vous voulez ins\u00e9rer une redirection pour ce fichier +jsite.project-files.custom-key=Clef existante +jsite.project-files.custom-key.tooltip=Utiliser une clef existante pour ce fichier +jsite.project-files.rename=Renommer +jsite.project-files.rename.tooltip=Renomme le fichier dans le site ins\u00e9r\u00e9 +jsite.project-files.mime-type=MIME type +jsite.project-files.mime-type.tooltip=S\u00e9lectionez le type MIME du fichier si la d\u00e9tection \u00e0 \u00e9chou\u00e9e +jsite.project-files.container=Container +jsite.project-files.container.tooltip=S\u00e9lectionnez un container pour le fichier +jsite.project-files.scan-error=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 \u00eatre selectionn\u00e9s 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}) +jsite.update-checker.latest-version.title=Recherche de mises \u00e0 jour +jsite.update-checker.latest-version.newer.message=Vous utilisez la version {0} mais une version
plus r\u00e9cente ({1}) est disponible! +jsite.update-checker.latest-version.older.message=Vous utilisez la version {0} mais la
derni\u00e8re version semble \u00eatre la {1}. +jsite.update-checker.latest-version.okay.message=Vous utilisez la version {0}
qui semble \u00eatre la derni\u00e8re. + +jsite.key-dialog.title=G\u00e9rer les cl\u00e9s des projets +jsite.key-dialog.button.ok.tooltip=Accepter les changements +jsite.key-dialog.button.cancel.tooltip=Annuler les changements +jsite.key-dialog.button.generate=Reg\u00e9n\u00e9rer les cl\u00e9s +jsite.key-dialog.button.generate.tooltip=Cr\u00e9er une nouvelle paire de cl\u00e9s +jsite.key-dialog.label.keys=Cl\u00e9s +jsite.key-dialog.label.private-key=Cl\u00e9 priv\u00e9e +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.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! +jsite.error.no-node-running=Ce noeud n'est pas actif!

Vous ne pouvez pas utiliser jSite sans noeud actif.
Veuillez d\u00e9marrer votre noeud et r\u00e9essayer. +jsite.error.no-local-path=Pas de chemin local sp\u00e9cifi\u00e9

Vous avez omis de sp\u00e9cifier le chemin local \u00e0 ins\u00e9rer. +jsite.error.no-path=Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite

Vous n'avez pas sp\u00e9cifi\u00e9 de chemin dans le freesite.
Ce champ est n\u00e9cessaire. +jsite.error.index-missing=Votre fichier par d\u00e9faut est manquant

Un fichier par d\u00e9faut est sp\u00e9cifi\u00e9 mais il
n'existe plus ! S\u00e9lectionnez un nouveau
fichier par d\u00e9faut dans la liste. +jsite.error.index-not-inserted=Fichier par d\u00e9faut non ins\u00e9r\u00e9

Vous avez choisi de ne pas ins\u00e9rer le fichier par d\u00e9faut !
Vous devez soit l'ins\u00e9rer soit choisir
un fichier par d\u00e9faut diff\u00e9rent ! +jsite.error.no-custom-key=Pas de clef existante sp\u00e9cifi\u00e9e pour ce fichier

Vous avez sp\u00e9cifier de ne pas ins\u00e9rer {0}
mais n'avez pas sp\u00e9cifier de clef ou rediriger! +jsite.error.no-files-to-insert=Aucun fichier \u00e0 ins\u00e9rer

Vous n'avez s\u00e9lectionn\u00e9 aucun fichier pour l'insertion !
Veuillez s\u00e9lectionner au moins un fichier \u00e0 ins\u00e9rer. +jsite.error.duplicate-file=Fichier dupliqu\u00e9

Le fichier {0} est ins\u00e9r\u00e9 deux fois !
Veuillez v\u00e9rifier les noms de fichier et les redirections. diff --git a/src/main/resources/flag-de.png b/src/main/resources/flag-de.png new file mode 100644 index 0000000..d1b572b Binary files /dev/null and b/src/main/resources/flag-de.png differ diff --git a/src/main/resources/flag-en.png b/src/main/resources/flag-en.png new file mode 100644 index 0000000..33472a7 Binary files /dev/null and b/src/main/resources/flag-en.png differ diff --git a/src/main/resources/flag-fr.png b/src/main/resources/flag-fr.png new file mode 100644 index 0000000..5793e70 Binary files /dev/null and b/src/main/resources/flag-fr.png differ diff --git a/src/main/resources/jsite-icon.png b/src/main/resources/jsite-icon.png new file mode 100644 index 0000000..fcddf6c Binary files /dev/null and b/src/main/resources/jsite-icon.png differ