diff --git a/build.xml b/build.xml index 4e6f88a..6b582f5 100644 --- a/build.xml +++ b/build.xml @@ -7,8 +7,8 @@ - - Builds, tests, and runs the project CHPJFix. + + Builds, tests, and runs the project JFix. - + @@ -55,6 +55,14 @@ is divided into following sections: + + + + + + + + @@ -69,8 +77,14 @@ is divided into following sections: + + + + + + @@ -83,10 +97,28 @@ is divided into following sections: - + - - + + + + + + + + + + + + + + + + + + + + @@ -155,7 +187,18 @@ is divided into following sections: - + + + + + + + + + + + + @@ -182,11 +225,13 @@ is divided into following sections: - + + + @@ -196,6 +241,7 @@ is divided into following sections: + @@ -207,10 +253,51 @@ is divided into following sections: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -227,14 +314,19 @@ is divided into following sections: Must set javac.includes - + + + - + + + + @@ -245,7 +337,8 @@ is divided into following sections: - + + @@ -261,11 +354,56 @@ is divided into following sections: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Must set JVM to use for profiling in profiler.info.jvm + Must set profiler agent JVM arguments in profiler.info.jvmargs.agent + @@ -358,14 +496,66 @@ is divided into following sections: + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -447,99 +643,65 @@ is divided into following sections: - + - + - + - To run this application from the command line without Ant, try: + To run this application from the command line without Ant, try: - java -cp "${run.classpath.with.dist.jar}" ${main.class} - - - - - - - - - - - - - - - - - - - - - - - To run this application from the command line without Ant, try: + java -cp "${run.classpath.with.dist.jar}" ${main.class} + + + + + + + + + + + + + + + + + + + + + + + + + To run this application from the command line without Ant, try: - java -jar "${dist.jar.resolved}" + java -jar "${dist.jar.resolved}" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - + + + + + + + + + + + Must select one file in the IDE or set profile.class + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + @@ -642,8 +879,8 @@ is divided into following sections: - - + + @@ -657,10 +894,10 @@ is divided into following sections: - + Must select some files in the IDE or set javac.includes - + @@ -766,7 +1003,7 @@ is divided into following sections: - + diff --git a/nbproject/edtFTPjDocList/package-list b/nbproject/edtFTPjDocList/package-list new file mode 100644 index 0000000..1440905 --- /dev/null +++ b/nbproject/edtFTPjDocList/package-list @@ -0,0 +1,7 @@ +com.enterprisedt +com.enterprisedt.net.ftp +com.enterprisedt.net.ftp.internal +com.enterprisedt.net.ftp.test +com.enterprisedt.util +com.enterprisedt.util.debug.util +com.enterprisedt.util.proxy diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index 643df6c..cc904d9 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=de15d164 -build.xml.script.CRC32=01d62e7e -build.xml.stylesheet.CRC32=958a1d3e@1.32.1.45 +build.xml.data.CRC32=323c66b9 +build.xml.script.CRC32=c9ee64b8 +build.xml.stylesheet.CRC32=28e38971@1.43.1.45 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=de15d164 -nbproject/build-impl.xml.script.CRC32=ff99b49a -nbproject/build-impl.xml.stylesheet.CRC32=576378a2@1.32.1.45 +nbproject/build-impl.xml.data.CRC32=323c66b9 +nbproject/build-impl.xml.script.CRC32=8ea475b6 +nbproject/build-impl.xml.stylesheet.CRC32=0ae3a408@1.44.1.45 diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties deleted file mode 100644 index e69de29..0000000 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties deleted file mode 100644 index 2e12dfc..0000000 --- a/nbproject/private/private.properties +++ /dev/null @@ -1,4 +0,0 @@ -application.args=ftp.chrissyx.com -compile.on.save=true -file.reference.edtftpj.jar=C:\\Programme\\Eclipse\\workspace\\CHPJFix\\lib\\edtftpj.jar -user.properties.file=C:\\Dokumente und Einstellungen\\Chrissyx\\.netbeans\\6.8\\build.properties diff --git a/nbproject/project.properties b/nbproject/project.properties index 6ea0f25..d1a1187 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -1,57 +1,10 @@ -application.desc=CHS - Java FTP Filetime Fix -application.homepage=http://www.chrissyx.de.vu -application.title=CHPJFix +annotation.processing.enabled=true +annotation.processing.enabled.in.editor=false +annotation.processing.run.all.processors=true +annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output +application.homepage=http://www.chrissyx.com/ +application.title=JFix application.vendor=Chrissyx -auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=true -auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=4 -auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=4 -auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=4 -auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80 -auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineAnnotationArgs=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineFor=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineImplements=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineMethodParams=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignMultilineThrows=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLinesAfterClassHeader=0 -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLinesAfterFields=1 -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classDeclBracePlacement=NEW_LINE -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.continuationIndentSize=4 -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.expand-tabs=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.indent-shift-width=4 -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.methodDeclBracePlacement=NEW_LINE -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.otherBracePlacement=NEW_LINE -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeCatchOnNewLine=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeElseOnNewLine=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeFinallyOnNewLine=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeWhileOnNewLine=true -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.redundantDoWhileBraces=LEAVE_ALONE -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.redundantForBraces=LEAVE_ALONE -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.redundantIfBraces=LEAVE_ALONE -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.redundantWhileBraces=LEAVE_ALONE -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceBeforeCatchParen=false -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceBeforeForParen=false -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceBeforeIfParen=false -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceBeforeSwitchParen=false -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceBeforeSynchronizedParen=false -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaceBeforeWhileParen=false -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.spaces-per-tab=4 -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.tab-size=4 -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.text-limit-width=80 -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapAnnotationArgs=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapArrayInit=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapChainedMethodCalls=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapDoWhileStatement=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapEnumConstants=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapExtendsImplementsKeyword=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapExtendsImplementsList=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapForStatement=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapIfStatement=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapMethodCallArgs=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapMethodParams=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapThrowsKeyword=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapThrowsList=WRAP_IF_LONG -auxiliary.org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.wrapWhileStatement=WRAP_IF_LONG build.classes.dir=${build.dir}/classes build.classes.excludes=**/*.java,**/*.form # This directory is removed when the project is cleaned: @@ -70,25 +23,38 @@ debug.test.classpath=\ ${run.test.classpath} # This directory is removed when the project is cleaned: dist.dir=dist -dist.jar=${dist.dir}/CHPJFix.jar +dist.jar=${dist.dir}/JFix.jar dist.javadoc.dir=${dist.dir}/javadoc endorsed.classpath= excludes= -file.reference.edtftpj.jar=lib/edtftpj.jar +file.reference.commons-codec.jar=lib\\commons-codec.jar +file.reference.edtftpj.jar=lib\\edtftpj.jar +file.reference.logback-classic.jar=lib/logback-classic.jar +file.reference.logback-core.jar=lib/logback-core.jar +file.reference.slf4j-api.jar=lib/slf4j-api.jar includes=** +jar.archive.disabled=${jnlp.enabled} jar.compress=false +jar.index=${jnlp.enabled} javac.classpath=\ - ${file.reference.edtftpj.jar} + ${file.reference.edtftpj.jar}:\ + ${file.reference.slf4j-api.jar}:\ + ${file.reference.logback-core.jar}:\ + ${file.reference.logback-classic.jar}:\ + ${file.reference.commons-codec.jar} # Space-separated list of extra javac options javac.compilerargs= javac.deprecation=true +javac.processorpath=\ + ${javac.classpath} javac.source=1.6 javac.target=1.6 javac.test.classpath=\ ${javac.classpath}:\ - ${build.classes.dir}:\ - ${libs.junit_4.classpath} -javadoc.additionalparam=-link http://java.sun.com/javase/6/docs/api -link http://www.enterprisedt.com/products/edtftpj/doc/api + ${build.classes.dir} +javac.test.processorpath=\ + ${javac.test.classpath} +javadoc.additionalparam=-link http://download.oracle.com/javase/6/docs/api/ -link http://www.enterprisedt.com/products/edtftpj/doc/api/ -link http://slf4j.org/apidocs/ -link http://logback.qos.ch/apidocs/ -link http://commons.apache.org/codec/api-release/ -linkoffline http://www.enterprisedt.com/products/edtftpj/doc/api/ C:\\Programme\\NetBeans\\projects\\JFix\\nbproject\\edtFTPjDocList\\ javadoc.author=true javadoc.encoding=${source.encoding} javadoc.noindex=false @@ -98,15 +64,20 @@ javadoc.private=true javadoc.splitindex=true javadoc.use=true javadoc.version=true -javadoc.windowtitle=CHS - Java FTP Filetime Fix -jnlp.codebase.type=local +javadoc.windowtitle=JFix +jnlp.codebase.type=no.codebase jnlp.descriptor=application jnlp.enabled=false +jnlp.mixed.code=default jnlp.offline-allowed=false jnlp.signed=false -main.class=com.chrissyx.jfix.JFixMain +jnlp.signing= +jnlp.signing.alias= +jnlp.signing.keystore= +main.class=com.chrissyx.jfix.JFix manifest.file=manifest.mf meta.inf.dir=${src.dir}/META-INF +mkdist.disabled=false platform.active=default_platform run.classpath=\ ${javac.classpath}:\ diff --git a/nbproject/project.xml b/nbproject/project.xml index 3fb62d1..ddf9831 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -3,7 +3,7 @@ org.netbeans.modules.java.j2seproject - CHPJFix + JFix diff --git a/src/com/chrissyx/jfix/JFix.java b/src/com/chrissyx/jfix/JFix.java index bf9aad4..e77121d 100644 --- a/src/com/chrissyx/jfix/JFix.java +++ b/src/com/chrissyx/jfix/JFix.java @@ -1,156 +1,58 @@ package com.chrissyx.jfix; -import com.chrissyx.jfix.common.error.JFixError; +import com.chrissyx.jfix.modules.GuiController; -import com.enterprisedt.net.ftp.FTPException; -import com.enterprisedt.net.ftp.FileTransferClient; +import javax.swing.SwingUtilities; -import java.io.File; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.LinkedList; +import org.slf4j.LoggerFactory; /** - * Java FTP Filetime Fix manages FTP access and - * fixes filetimes not possible to set with MDTM or MFMT with SITE UTIME. - * Uses edtFTPj/Free as FTP client. + * Main class and application entry point of JFix. + * + * Java FTP Filetime Fix manages FTP access and fixes timestamps of remote files. + * Filetimes are set with FTP commands defined by the bundled plug-ins, e.g. SITE UTIME instead of unsupported MDTM or MFMT server commands. * * @author Chrissyx - * @see edtFTPj/Free Homepage + * @version 1.0 */ public class JFix { /** - * The FTP client being worked with. - */ - private FileTransferClient ftpClient; - - /** - * Date formatter to prepare the filetimes. - */ - private final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); - - /** - * Initializes the FTP client module. - * - * @param host Host address to conenct to - * @param user User name for authentication - * @param pass Password of user - * @throws JFixError If setting up FTP client failed - */ - public JFix(final String host, final String user, final String pass) throws - JFixError - { - this.ftpClient = new FileTransferClient(); - try - { - this.ftpClient.setRemoteHost(host); - this.ftpClient.setUserName(user); - this.ftpClient.setPassword(pass); - } - catch(final FTPException e) - { - throw new JFixError("Can't setup FTP client!", e); - } - JFixMain.getLogger().info("JFix initialized!"); - } - - /** - * Connects to FTP host. - * - * @throws JFixError If connecting failed + * Current version number of JFix. */ - public void connect() throws JFixError - { - try - { - this.ftpClient.connect(); - } - catch(final FTPException e) - { - throw new JFixError("Can't connect to FTP host!", e); - } - catch(final IOException e) - { - throw new JFixError(e); - } - JFixMain.getLogger().info( - "Connected to " + this.ftpClient.getRemoteHost() + "!"); - } + public static final String VERSION = "1.0"; /** - * Disconnects from FTP host. - * - * @throws JFixError If disconnecting failed + * Hidden constructor to prevent instances of this class. */ - public void disconnect() throws JFixError + private JFix() { - try - { - this.ftpClient.disconnect(); - } - catch(final FTPException e) - { - throw new JFixError("Can't disconnect from FTP host!", e); - } - catch(final IOException e) - { - throw new JFixError(e); - } - JFixMain.getLogger().info("Disconnected from " + this.ftpClient. - getRemoteHost() + "!"); } /** - * Fixes the filestimes of current local files with the ones from the host. + * Starts application with GUI by calling {@link GuiController}. * - * @param localDir Locale directory to gather the files' timestamp - * @param remoteDir Optional remote directory to change to first - * @throws JFixError If executing FTP command failed + * @param args Unused arguments */ - public void fixFiletimes(final String localDir, final String remoteDir) - throws JFixError + public static void main(final String[] args) { - if(remoteDir != null) - try - { - this.ftpClient.changeDirectory(remoteDir); - } - catch(final FTPException e) - { - throw new JFixError( - "Can't change remote directory to '" + remoteDir + "'!", e); - } - catch(final IOException e) - { - throw new JFixError(e); - } - final LinkedList commands = new LinkedList(); - //Create command list - for(final File curFile : new File(localDir).listFiles()) - { - final String curTime = this.sdf.format(new Date( - curFile.lastModified())); - commands.add("SITE UTIME " + curFile.getName() + " " + curTime + " " - + curTime + " " + curTime + " UTC"); - } - //Execute commands - for(final String curCommand : commands) - try - { - JFixMain.getLogger().info("Executing '" + curCommand + "'...\n" - + this.ftpClient.executeCommand(curCommand)); - } - catch(final FTPException e) - { - throw new JFixError( - "Can't execute '" + curCommand + "' command!", e); - } - catch(final IOException e) + LoggerFactory.getLogger(JFix.class).info("Starting JFix {}...", JFix.VERSION); + LoggerFactory.getLogger(JFix.class).info("Running on {} {} with Java {}", new Object[] + { + System.getProperty("os.name"), + System.getProperty("os.version"), + System.getProperty("java.version") + }); + SwingUtilities.invokeLater(new Runnable() + { + /** + * {@inheritDoc} + */ + @Override + public void run() { - throw new JFixError(e); + GuiController.getInstance(); } - JFixMain.getLogger().info("Executed " + commands.size() + " commands!"); + }); } } diff --git a/src/com/chrissyx/jfix/JFixMain.java b/src/com/chrissyx/jfix/JFixMain.java deleted file mode 100644 index 654d834..0000000 --- a/src/com/chrissyx/jfix/JFixMain.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.chrissyx.jfix; - -import com.chrissyx.jfix.common.error.JFixError; - -import java.io.IOException; -import java.util.logging.FileHandler; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; - -/** - * Perfoms one run of JFix with logging. - * - * @author Chrissyx - * @version 0.1 - * @see Chrissyx Homepage - */ -public class JFixMain -{ - /** - * Static instance of the logger. - */ - private static Logger logger; - - /** - * Hidden constructor to prevent instances of this class. - */ - private JFixMain() - { - } - - /** - * Creates JFix instance and performs sync of filetimes. - * - * @param args Contains host, user and pass - */ - public static void main(final String[] args) - { - if(args.length < 4) - System.out.println( - "Usage: CHSJFix []"); - else - { - try - { - final JFix jFix = new JFix(args[0], args[1], args[2]); - jFix.connect(); - jFix.fixFiletimes(args[3], args.length < 5 ? null : args[4]); - jFix.disconnect(); - } - catch(final JFixError e) - { - JFixMain.logger.severe(e.getLogMessage()); - e.printStackTrace(); - System.exit(-1); - } - } - } - - /** - * Returns an instance of the logger. Loglevel is hard-coded. - * - * @return Logger instance - * @see java.util.logging.Logger - */ - public static Logger getLogger() - { - if(JFixMain.logger == null) - { - JFixMain.logger = Logger.getLogger(JFixMain.class.getName()); - try - { - //Log in TEMP folder - final FileHandler fh = new FileHandler("%t/jfix.log"); - fh.setFormatter(new SimpleFormatter()); - JFixMain.logger.addHandler(fh); - } - catch(final IOException e) - { - //Only console available... - JFixMain.logger.log(Level.WARNING, e.getMessage(), e); - } - JFixMain.logger.setLevel(Level.ALL); //Set level here - JFixMain.logger.fine("Logger initialized!"); - } - return JFixMain.logger; - } -} diff --git a/src/com/chrissyx/jfix/common/error/JFixError.java b/src/com/chrissyx/jfix/common/error/JFixError.java index e0187a4..2cf0ebd 100644 --- a/src/com/chrissyx/jfix/common/error/JFixError.java +++ b/src/com/chrissyx/jfix/common/error/JFixError.java @@ -1,5 +1,9 @@ package com.chrissyx.jfix.common.error; +import com.chrissyx.jfix.modules.GuiController; + +import org.slf4j.LoggerFactory; + /** * General JFix error. * @@ -10,7 +14,7 @@ public class JFixError extends Exception /** * Custom log message. */ - private final String logMessage; + protected final String logMessage; /** * Creates an exception. @@ -64,4 +68,30 @@ public String getLogMessage() { return this.logMessage; } + + /** + * Logs this error for stated class with a error severity and displays it in an error dialog. + * + * @param clazz Class this error was thrown from + */ + public void error(final Class clazz) + { + final String curLogMessage = this.logMessage == null ? super.getMessage() : this.logMessage; + LoggerFactory.getLogger(clazz).error(curLogMessage, super.getCause()); + GuiController.getInstance().showErrorDialog(curLogMessage); + } + + /** + * Logs this error for stated class with a warning severity. + * + * @param clazz Class this error was thrown from + * @param showDialog Show this error in a warning dialog + */ + public void warn(final Class clazz, final boolean showDialog) + { + final String curLogMessage = this.logMessage == null ? super.getMessage() : this.logMessage; + LoggerFactory.getLogger(clazz).warn(curLogMessage, super.getCause()); + if(showDialog) + GuiController.getInstance().showWarningDialog(curLogMessage); + } } diff --git a/src/com/chrissyx/jfix/common/error/JFixFtpAuthError.java b/src/com/chrissyx/jfix/common/error/JFixFtpAuthError.java new file mode 100644 index 0000000..371023d --- /dev/null +++ b/src/com/chrissyx/jfix/common/error/JFixFtpAuthError.java @@ -0,0 +1,21 @@ +package com.chrissyx.jfix.common.error; + +/** + * Problems with authentication of remote server. + * + * @author Chrissyx + * @since 0.2 + */ +public class JFixFtpAuthError extends JFixError +{ + /** + * Creates an exception with a custom log message and original error. + * + * @param sLogMessage Custom log message + * @param e Original exception + */ + public JFixFtpAuthError(final String sLogMessage, final Throwable e) + { + super(sLogMessage, e); + } +} diff --git a/src/com/chrissyx/jfix/common/error/package-info.java b/src/com/chrissyx/jfix/common/error/package-info.java new file mode 100644 index 0000000..53ea0af --- /dev/null +++ b/src/com/chrissyx/jfix/common/error/package-info.java @@ -0,0 +1,6 @@ +/** + * Contains all custom error classes. Every error inherits from {@link com.chrissyx.jfix.common.error.JFixError}. + * + * @author Chrissyx + */ +package com.chrissyx.jfix.common.error; diff --git a/src/com/chrissyx/jfix/common/error/package.html b/src/com/chrissyx/jfix/common/error/package.html deleted file mode 100644 index 5dd582e..0000000 --- a/src/com/chrissyx/jfix/common/error/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - - -com.chrissyx.jfix.common.error - - -Contains all custom error classes. Every error inherits from {@link com.chrissyx.jfix.common.error.JFixError}. - - \ No newline at end of file diff --git a/src/com/chrissyx/jfix/gui/BaseView.java b/src/com/chrissyx/jfix/gui/BaseView.java new file mode 100644 index 0000000..8aa53ba --- /dev/null +++ b/src/com/chrissyx/jfix/gui/BaseView.java @@ -0,0 +1,19 @@ +package com.chrissyx.jfix.gui; + +/** + * The root interface for all frames. + * + * @author Chrissyx + */ +public interface BaseView +{ + /** + * Closes the frame. + */ + void closeFrame(); + + /** + * Displays the frame. + */ + void showFrame(); +} diff --git a/src/com/chrissyx/jfix/gui/MainView.java b/src/com/chrissyx/jfix/gui/MainView.java new file mode 100644 index 0000000..74835c7 --- /dev/null +++ b/src/com/chrissyx/jfix/gui/MainView.java @@ -0,0 +1,35 @@ +package com.chrissyx.jfix.gui; + +/** + * The main frame. + * + * @author Chrissyx + */ +public interface MainView extends BaseView +{ + /** + * Appends a log entry to text area console. + * + * @param logMessage Log entry to append + */ + void appendLogEntry(final String logMessage); + + /** + * Clears all log entries from text area console. + */ + void clearLogConsole(); + + /** + * Sets local filenames as items to listbox. + * + * @param filenames Filenames to set + */ + void setLocalFileList(final String[] filenames); + + /** + * Sets remote filenames as items to listbox. + * + * @param filenames Filenames to set + */ + void setRemoteFileList(final String[] filenames); +} diff --git a/src/com/chrissyx/jfix/gui/OptionsView.java b/src/com/chrissyx/jfix/gui/OptionsView.java new file mode 100644 index 0000000..2e166f8 --- /dev/null +++ b/src/com/chrissyx/jfix/gui/OptionsView.java @@ -0,0 +1,72 @@ +package com.chrissyx.jfix.gui; + +/** + * The options frame. + * + * @author Chrissyx + */ +public interface OptionsView extends BaseView +{ + /** + * Returns option value for host. + * + * @return Host option value + */ + String getOptionHost(); + + /** + * Returns option value for port number. + * + * @return Port number option value + */ + String getOptionPort(); + + /** + * Returns option value for user name. + * + * @return User name option value + */ + String getOptionUser(); + + /** + * Returns option value for password. + * + * @return Password option value + */ + String getOptionPass(); + + /** + * Returns option value for selected language. + * + * @return New language to use + */ + String getOptionLanguage(); + + /** + * Returns option value for local start directory. + * + * @return Local start directory option value + */ + String getOptionLocalStartDir(); + + /** + * Returns option value for remote start directory. + * + * @return Remote start directory option value + */ + String getOptionRemoteStartDir(); + + /** + * Returns option value for selected plug-in. + * + * @return Plug-in to use + */ + String getOptionPlugIn(); + + /** + * Returns option value for fecursive fixing of filetimes. + * + * @return Fix filetimes recursively + */ + boolean getOptionRecursive(); +} diff --git a/src/com/chrissyx/jfix/gui/impl/MainFrame.form b/src/com/chrissyx/jfix/gui/impl/MainFrame.form new file mode 100644 index 0000000..1f13285 --- /dev/null +++ b/src/com/chrissyx/jfix/gui/impl/MainFrame.form @@ -0,0 +1,177 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/chrissyx/jfix/gui/impl/MainFrame.java b/src/com/chrissyx/jfix/gui/impl/MainFrame.java new file mode 100644 index 0000000..a838f6c --- /dev/null +++ b/src/com/chrissyx/jfix/gui/impl/MainFrame.java @@ -0,0 +1,249 @@ +package com.chrissyx.jfix.gui.impl; + +import com.chrissyx.jfix.gui.MainView; +import com.chrissyx.jfix.modules.GuiController; +import com.chrissyx.jfix.modules.LangController; +import com.chrissyx.jfix.modules.util.ResourceUtils; + +/** + * The main window of this application. + * + * @author Chrissyx + */ +public class MainFrame extends javax.swing.JFrame implements MainView +{ + /** + * Creates new main frame. + */ + public MainFrame() + { + this.initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jScrollPane1 = new javax.swing.JScrollPane(); + lstLocalFiles = new javax.swing.JList(); + jScrollPane2 = new javax.swing.JScrollPane(); + lstRemoteFiles = new javax.swing.JList(); + jScrollPane3 = new javax.swing.JScrollPane(); + txtAreaLog = new javax.swing.JTextArea(); + btnConnect = new javax.swing.JToggleButton(); + btnOptions = new javax.swing.JButton(); + btnExit = new javax.swing.JButton(); + btnFixIt = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + setTitle("JFix"); + setIconImage(ResourceUtils.getImage("icon.png")); + setName("mainFrame"); // NOI18N + addWindowListener(new java.awt.event.WindowAdapter() { + public void windowClosed(java.awt.event.WindowEvent evt) { + formWindowClosed(evt); + } + }); + + lstLocalFiles.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + lstLocalFiles.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + lstLocalFilesMouseClicked(evt); + } + }); + jScrollPane1.setViewportView(lstLocalFiles); + + lstRemoteFiles.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + lstRemoteFiles.setEnabled(false); + lstRemoteFiles.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + lstRemoteFilesMouseClicked(evt); + } + }); + jScrollPane2.setViewportView(lstRemoteFiles); + + txtAreaLog.setColumns(20); + txtAreaLog.setFont(new java.awt.Font("Monospaced", 0, 10)); + txtAreaLog.setRows(5); + jScrollPane3.setViewportView(txtAreaLog); + + btnConnect.setText(LangController.getInstance().getString("connect")); + btnConnect.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnConnectActionPerformed(evt); + } + }); + + btnOptions.setText(LangController.getInstance().getString("options")); + btnOptions.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnOptionsActionPerformed(evt); + } + }); + + btnExit.setText(LangController.getInstance().getString("exit")); + btnExit.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnExitActionPerformed(evt); + } + }); + + btnFixIt.setText(LangController.getInstance().getString("fix_it")); + btnFixIt.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnFixItActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(jScrollPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 475, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(btnExit, javax.swing.GroupLayout.PREFERRED_SIZE, 75, Short.MAX_VALUE) + .addComponent(btnOptions, javax.swing.GroupLayout.PREFERRED_SIZE, 75, Short.MAX_VALUE) + .addComponent(btnFixIt, javax.swing.GroupLayout.PREFERRED_SIZE, 75, Short.MAX_VALUE) + .addComponent(btnConnect, javax.swing.GroupLayout.Alignment.TRAILING))) + .addGroup(layout.createSequentialGroup() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 285, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 285, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 164, Short.MAX_VALUE) + .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 164, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addGroup(layout.createSequentialGroup() + .addComponent(btnConnect) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnFixIt) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnOptions) + .addGap(4, 4, 4) + .addComponent(btnExit)) + .addComponent(jScrollPane3)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void btnConnectActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_btnConnectActionPerformed + {//GEN-HEADEREND:event_btnConnectActionPerformed + this.btnConnect.setSelected(this.btnConnect.isSelected() ? GuiController.getInstance().onConnect() : !GuiController.getInstance().onDisconnect()); + this.lstRemoteFiles.setEnabled(this.btnConnect.isSelected()); + }//GEN-LAST:event_btnConnectActionPerformed + + private void btnFixItActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_btnFixItActionPerformed + {//GEN-HEADEREND:event_btnFixItActionPerformed + GuiController.getInstance().onFixFiletimes(); + }//GEN-LAST:event_btnFixItActionPerformed + + private void btnOptionsActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_btnOptionsActionPerformed + {//GEN-HEADEREND:event_btnOptionsActionPerformed + GuiController.getInstance().onShowOptions(); + }//GEN-LAST:event_btnOptionsActionPerformed + + private void btnExitActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_btnExitActionPerformed + {//GEN-HEADEREND:event_btnExitActionPerformed + GuiController.getInstance().onClose(); + }//GEN-LAST:event_btnExitActionPerformed + + private void formWindowClosed(java.awt.event.WindowEvent evt)//GEN-FIRST:event_formWindowClosed + {//GEN-HEADEREND:event_formWindowClosed + GuiController.getInstance().onClose(); + }//GEN-LAST:event_formWindowClosed + +private void lstLocalFilesMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lstLocalFilesMouseClicked + if(evt.getClickCount() == 2) + GuiController.getInstance().onChangeLocalDir(this.lstLocalFiles.getModel().getElementAt(this.lstLocalFiles.locationToIndex(evt.getPoint())).toString()); +}//GEN-LAST:event_lstLocalFilesMouseClicked + +private void lstRemoteFilesMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lstRemoteFilesMouseClicked + if(evt.getClickCount() == 2 && this.lstRemoteFiles.getModel().getSize() != 0) + GuiController.getInstance().onChangeRemoteDir(this.lstRemoteFiles.getModel().getElementAt(this.lstRemoteFiles.locationToIndex(evt.getPoint())).toString()); +}//GEN-LAST:event_lstRemoteFilesMouseClicked + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JToggleButton btnConnect; + private javax.swing.JButton btnExit; + private javax.swing.JButton btnFixIt; + private javax.swing.JButton btnOptions; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JScrollPane jScrollPane2; + private javax.swing.JScrollPane jScrollPane3; + private javax.swing.JList lstLocalFiles; + private javax.swing.JList lstRemoteFiles; + private javax.swing.JTextArea txtAreaLog; + // End of variables declaration//GEN-END:variables + + /** + * {@inheritDoc} + */ + @Override + public void closeFrame() + { + this.dispose(); + } + + /** + * {@inheritDoc} + */ + @Override + public void showFrame() + { + this.setVisible(true); + } + + /** + * {@inheritDoc} + */ + @Override + public void appendLogEntry(final String logMessage) + { + this.txtAreaLog.append(logMessage + "\n"); + } + + /** + * {@inheritDoc} + */ + @Override + public void clearLogConsole() + { + this.txtAreaLog.setText(null); + } + + /** + * {@inheritDoc} + */ + @Override + public void setLocalFileList(final String[] filenames) + { + this.lstLocalFiles.setListData(filenames); + } + + /** + * {@inheritDoc} + */ + @Override + public void setRemoteFileList(final String[] filenames) + { + this.lstRemoteFiles.setListData(filenames); + } +} diff --git a/src/com/chrissyx/jfix/gui/impl/OptionsDialog.form b/src/com/chrissyx/jfix/gui/impl/OptionsDialog.form new file mode 100644 index 0000000..d9a4333 --- /dev/null +++ b/src/com/chrissyx/jfix/gui/impl/OptionsDialog.form @@ -0,0 +1,352 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/chrissyx/jfix/gui/impl/OptionsDialog.java b/src/com/chrissyx/jfix/gui/impl/OptionsDialog.java new file mode 100644 index 0000000..d128ebb --- /dev/null +++ b/src/com/chrissyx/jfix/gui/impl/OptionsDialog.java @@ -0,0 +1,394 @@ +package com.chrissyx.jfix.gui.impl; + +import com.chrissyx.jfix.common.error.JFixError; +import com.chrissyx.jfix.gui.OptionsView; +import com.chrissyx.jfix.modules.ConfigController; +import com.chrissyx.jfix.modules.GuiController; +import com.chrissyx.jfix.modules.LangController; +import com.chrissyx.jfix.modules.util.CryptUtils; +import com.chrissyx.jfix.modules.util.FileUtils; + +import javax.swing.JFileChooser; + +import org.slf4j.LoggerFactory; + +/** + * The options dialog. + * + * @author Chrissyx + */ +public class OptionsDialog extends javax.swing.JDialog implements OptionsView +{ + /** + * Creates new options dialog. + * + * @param parent Parent frame of this dialog + * @param modal Modal dialog mode + */ + public OptionsDialog(final java.awt.Frame parent, final boolean modal) + { + super(parent, modal); + this.initComponents(); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + pnlFTP = new javax.swing.JPanel(); + lblHost = new javax.swing.JLabel(); + lblPort = new javax.swing.JLabel(); + lblUser = new javax.swing.JLabel(); + lblPass = new javax.swing.JLabel(); + txtHost = new javax.swing.JTextField(); + fmtTxtPort = new javax.swing.JFormattedTextField(); + txtUser = new javax.swing.JTextField(); + pwdPass = new javax.swing.JPasswordField(); + pnlGeneral = new javax.swing.JPanel(); + lblLocalStartDir = new javax.swing.JLabel(); + txtLocalStartDir = new javax.swing.JTextField(); + lblRemoteStartDir = new javax.swing.JLabel(); + txtRemoteStartDir = new javax.swing.JTextField(); + btnSelectLocalDir = new javax.swing.JButton(); + chkRecursive = new javax.swing.JCheckBox(); + lblPlugIn = new javax.swing.JLabel(); + cbxPlugIns = new javax.swing.JComboBox(); + lblLanguage = new javax.swing.JLabel(); + cbxLanguage = new javax.swing.JComboBox(); + btnOK = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle(LangController.getInstance().getString("options")); + setName("optionsDialog"); // NOI18N + setResizable(false); + + pnlFTP.setBorder(javax.swing.BorderFactory.createTitledBorder(LangController.getInstance().getString("ftp_connection"))); + + lblHost.setText(LangController.getInstance().getString("host")); + + lblPort.setText(LangController.getInstance().getString("port_number")); + + lblUser.setText(LangController.getInstance().getString("user_name")); + + lblPass.setText(LangController.getInstance().getString("password")); + + txtHost.setText(ConfigController.getInstance().getCfgVal("host")); + + fmtTxtPort.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.NumberFormatter(new java.text.DecimalFormat("#0")))); + fmtTxtPort.setText(ConfigController.getInstance().getCfgVal("port")); + + txtUser.setText(ConfigController.getInstance().getCfgVal("user")); + + pwdPass.setText(CryptUtils.decode(ConfigController.getInstance().getCfgVal("pass"))); + + javax.swing.GroupLayout pnlFTPLayout = new javax.swing.GroupLayout(pnlFTP); + pnlFTP.setLayout(pnlFTPLayout); + pnlFTPLayout.setHorizontalGroup( + pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnlFTPLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblHost) + .addComponent(lblPort) + .addComponent(lblUser) + .addComponent(lblPass)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pwdPass, javax.swing.GroupLayout.DEFAULT_SIZE, 272, Short.MAX_VALUE) + .addComponent(txtUser, javax.swing.GroupLayout.DEFAULT_SIZE, 272, Short.MAX_VALUE) + .addComponent(txtHost, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 272, Short.MAX_VALUE) + .addComponent(fmtTxtPort, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 272, Short.MAX_VALUE)) + .addContainerGap()) + ); + pnlFTPLayout.setVerticalGroup( + pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnlFTPLayout.createSequentialGroup() + .addGroup(pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblHost) + .addComponent(txtHost, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPort) + .addComponent(fmtTxtPort, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblUser) + .addComponent(txtUser, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnlFTPLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblPass) + .addComponent(pwdPass, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + ); + + pnlGeneral.setBorder(javax.swing.BorderFactory.createTitledBorder(LangController.getInstance().getString("general"))); + + lblLocalStartDir.setText(LangController.getInstance().getString("local_start_directory")); + + txtLocalStartDir.setEditable(false); + txtLocalStartDir.setText(ConfigController.getInstance().getCfgVal("localStartDir")); + + lblRemoteStartDir.setText(LangController.getInstance().getString("remote_start_directory")); + + txtRemoteStartDir.setText(ConfigController.getInstance().getCfgVal("remoteStartDir")); + + btnSelectLocalDir.setText(LangController.getInstance().getString("dots")); + btnSelectLocalDir.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnSelectLocalDirActionPerformed(evt); + } + }); + + chkRecursive.setSelected(Boolean.parseBoolean(ConfigController.getInstance().getCfgVal("recursive"))); + chkRecursive.setText(LangController.getInstance().getString("fix_filetimes_recursively")); + + lblPlugIn.setText(LangController.getInstance().getString("plug_in_to_use")); + + cbxPlugIns.setModel(new javax.swing.DefaultComboBoxModel(FileUtils.getPackageListing("plugins", ".class", "FiletimeFixer", "package-info"))); + cbxPlugIns.setSelectedItem(ConfigController.getInstance().getCfgVal("plugIn")); + + lblLanguage.setText(LangController.getInstance().getString("language_after_restart")); + + cbxLanguage.setModel(new javax.swing.DefaultComboBoxModel(LangController.getInstance().getLocales())); + cbxLanguage.setSelectedItem(ConfigController.getInstance().getCfgVal("language")); + + javax.swing.GroupLayout pnlGeneralLayout = new javax.swing.GroupLayout(pnlGeneral); + pnlGeneral.setLayout(pnlGeneralLayout); + pnlGeneralLayout.setHorizontalGroup( + pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnlGeneralLayout.createSequentialGroup() + .addContainerGap() + .addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(chkRecursive, javax.swing.GroupLayout.DEFAULT_SIZE, 348, Short.MAX_VALUE) + .addGroup(pnlGeneralLayout.createSequentialGroup() + .addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lblRemoteStartDir) + .addComponent(lblLocalStartDir) + .addComponent(lblPlugIn) + .addComponent(lblLanguage)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(txtRemoteStartDir, javax.swing.GroupLayout.DEFAULT_SIZE, 272, Short.MAX_VALUE) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pnlGeneralLayout.createSequentialGroup() + .addComponent(txtLocalStartDir, javax.swing.GroupLayout.DEFAULT_SIZE, 243, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnSelectLocalDir, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(cbxPlugIns, 0, 272, Short.MAX_VALUE) + .addComponent(cbxLanguage, 0, 272, Short.MAX_VALUE)))) + .addContainerGap()) + ); + pnlGeneralLayout.setVerticalGroup( + pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(pnlGeneralLayout.createSequentialGroup() + .addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cbxLanguage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblLanguage)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblLocalStartDir) + .addComponent(txtLocalStartDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(btnSelectLocalDir)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblRemoteStartDir) + .addComponent(txtRemoteStartDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(pnlGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cbxPlugIns, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblPlugIn)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(chkRecursive)) + ); + + btnOK.setText(LangController.getInstance().getString("save")); + btnOK.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnOKActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(150, 150, 150) + .addComponent(btnOK)) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pnlGeneral, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pnlFTP, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(pnlFTP, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pnlGeneral, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(btnOK) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + +private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed + GuiController.getInstance().onSaveOptions(); +}//GEN-LAST:event_btnOKActionPerformed + +private void btnSelectLocalDirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSelectLocalDirActionPerformed + LoggerFactory.getLogger(OptionsDialog.class).debug("Creating file chooser dialog for directory..."); + final JFileChooser fileChooser = new JFileChooser(this.txtLocalStartDir.getText()); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + switch(fileChooser.showOpenDialog(this)) + { + case JFileChooser.APPROVE_OPTION: + this.txtLocalStartDir.setText(fileChooser.getSelectedFile().getPath()); + break; + + case JFileChooser.CANCEL_OPTION: + LoggerFactory.getLogger(OptionsDialog.class).debug("User cancelled directory choose dialog"); + break; + + case JFileChooser.ERROR_OPTION: + new JFixError("Showing open dialog for choosing directory failed!").error(OptionsDialog.class); + break; + + default: + LoggerFactory.getLogger(OptionsDialog.class).warn("Unknown return value from open dialog for choosing directory!"); + break; + } +}//GEN-LAST:event_btnSelectLocalDirActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton btnOK; + private javax.swing.JButton btnSelectLocalDir; + private javax.swing.JComboBox cbxLanguage; + private javax.swing.JComboBox cbxPlugIns; + private javax.swing.JCheckBox chkRecursive; + private javax.swing.JFormattedTextField fmtTxtPort; + private javax.swing.JLabel lblHost; + private javax.swing.JLabel lblLanguage; + private javax.swing.JLabel lblLocalStartDir; + private javax.swing.JLabel lblPass; + private javax.swing.JLabel lblPlugIn; + private javax.swing.JLabel lblPort; + private javax.swing.JLabel lblRemoteStartDir; + private javax.swing.JLabel lblUser; + private javax.swing.JPanel pnlFTP; + private javax.swing.JPanel pnlGeneral; + private javax.swing.JPasswordField pwdPass; + private javax.swing.JTextField txtHost; + private javax.swing.JTextField txtLocalStartDir; + private javax.swing.JTextField txtRemoteStartDir; + private javax.swing.JTextField txtUser; + // End of variables declaration//GEN-END:variables + + /** + * {@inheritDoc} + */ + @Override + public void closeFrame() + { + this.dispose(); + } + + /** + * {@inheritDoc} + */ + @Override + public void showFrame() + { + this.setVisible(true); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionHost() + { + return this.txtHost.getText(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionPort() + { + return this.fmtTxtPort.getText(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionUser() + { + return this.txtUser.getText(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionPass() + { + return new String(this.pwdPass.getPassword()); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionLanguage() + { + return (String) this.cbxLanguage.getSelectedItem(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionLocalStartDir() + { + return this.txtLocalStartDir.getText(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionRemoteStartDir() + { + return this.txtRemoteStartDir.getText(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getOptionPlugIn() + { + return (String) this.cbxPlugIns.getSelectedItem(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean getOptionRecursive() + { + return this.chkRecursive.isSelected(); + } +} diff --git a/src/com/chrissyx/jfix/gui/impl/package-info.java b/src/com/chrissyx/jfix/gui/impl/package-info.java new file mode 100644 index 0000000..85ae7a5 --- /dev/null +++ b/src/com/chrissyx/jfix/gui/impl/package-info.java @@ -0,0 +1,6 @@ +/** + * Contains the concrete GUI implementation as defined by the interfaces from the parent package. + * + * @author Chrissyx + */ +package com.chrissyx.jfix.gui.impl; diff --git a/src/com/chrissyx/jfix/gui/package-info.java b/src/com/chrissyx/jfix/gui/package-info.java new file mode 100644 index 0000000..f476093 --- /dev/null +++ b/src/com/chrissyx/jfix/gui/package-info.java @@ -0,0 +1,7 @@ +/** + * Contains GUI interfaces for a concrete implementation, like {@code com.chrissyx.jfix.gui.impl}. + * + * @author Chrissyx + * @since 0.2 + */ +package com.chrissyx.jfix.gui; diff --git a/src/com/chrissyx/jfix/modules/ConfigController.java b/src/com/chrissyx/jfix/modules/ConfigController.java new file mode 100644 index 0000000..9d6c39d --- /dev/null +++ b/src/com/chrissyx/jfix/modules/ConfigController.java @@ -0,0 +1,133 @@ +package com.chrissyx.jfix.modules; + +import com.chrissyx.jfix.common.error.JFixError; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Observable; +import java.util.Properties; + +import org.slf4j.LoggerFactory; + +/** + * Manages configuration values. + * + * @author Chrissyx + * @since 0.2 + */ +public final class ConfigController extends Observable +{ + /** + * Singleton instance of this class. + */ + private static ConfigController configController = new ConfigController(); + + /** + * Properties handler. + */ + private final Properties properties; + + /** + * Name of XML configuration file. + */ + private final String configFile; + + /** + * Creates properties handler. + */ + private ConfigController() + { + this.properties = new Properties(); + this.configFile = System.getProperty("user.dir") + File.separator + "jfix-config.xml"; + LoggerFactory.getLogger(ConfigController.class).debug("Loading settings from {}...", this.configFile); + try + { + this.properties.loadFromXML(new FileInputStream(this.configFile)); + LoggerFactory.getLogger(ConfigController.class).info("Loaded {} settings!", this.properties.size()); + } + catch(final FileNotFoundException e) + { + LoggerFactory.getLogger(ConfigController.class).warn("Config XML file not found, loading defaults...", e); + this.setCfgVal("port", "21"); + this.setCfgVal("localStartDir", System.getProperty("user.dir")); + this.setCfgVal("remoteStartDir", "/"); + this.setCfgVal("plugIn", "SiteUtime"); + this.setCfgVal("recursive", "false"); + this.setCfgVal("language", "en_US"); + } + catch(final IOException e) + { + new JFixError("Cannot access config XML file!", e).error(ConfigController.class); + } + } + + /** + * Returns the singleton instance of this class. + * + * @return Instance of this class + */ + public static ConfigController getInstance() + { + return ConfigController.configController; + } + + /** + * Returns a single configuration value. + * + * @param key Identifier of config value + * @return Requested config value or empty string + * @see #hasCfgVal(java.lang.String) + */ + public String getCfgVal(final String key) + { + LoggerFactory.getLogger(LangController.class).debug("Returning config value for key '{}'...", key); + return this.properties.getProperty(key, ""); + } + + /** + * Returns the stated configuration identifier is known. + * + * @param key Identifier of config value + * @return Requested config value exists + */ + public boolean hasCfgVal(final String key) + { + return this.properties.containsKey(key); + } + + /** + * Sets a single configuration value. + * + * @param key Identifier to access the value + * @param value Configuration entry + */ + public void setCfgVal(final String key, final String value) + { + this.properties.setProperty(key, value); + } + + /** + * Saves configuration values to file and notifies observing controllers. + * + * @throws JFixError If saving failed + */ + public void save() throws JFixError + { + LoggerFactory.getLogger(ConfigController.class).debug("Saving settings..."); + try + { + this.properties.storeToXML(new FileOutputStream(this.configFile), null); + } + catch(final IOException e) + { + throw new JFixError("Cannot save settings to file!", e); + } + LoggerFactory.getLogger(ConfigController.class).info("Settings saved!"); + //Notify regged controllers about new config + this.setChanged(); + this.notifyObservers(); + } +} diff --git a/src/com/chrissyx/jfix/modules/FtpController.java b/src/com/chrissyx/jfix/modules/FtpController.java new file mode 100644 index 0000000..db4aff2 --- /dev/null +++ b/src/com/chrissyx/jfix/modules/FtpController.java @@ -0,0 +1,424 @@ +package com.chrissyx.jfix.modules; + +import com.chrissyx.jfix.common.error.JFixError; +import com.chrissyx.jfix.common.error.JFixFtpAuthError; +import com.chrissyx.jfix.modules.util.CryptUtils; +import com.chrissyx.jfix.plugins.FiletimeFixer; +import com.enterprisedt.net.ftp.EventListener; +import com.enterprisedt.net.ftp.FTPException; +import com.enterprisedt.net.ftp.FTPFile; +import com.enterprisedt.net.ftp.FileTransferClient; + +import java.io.File; +import java.io.IOException; +import java.text.ParseException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.Observable; +import java.util.Observer; + +import org.slf4j.LoggerFactory; + +/** + * Manages FTP access. + * + * @author Chrissyx + * @see edtFTPj/Free Homepage + */ +public final class FtpController implements Observer +{ + /** + * Singleton instance of this class. + */ + private static FtpController ftpController = new FtpController(); + + /** + * The FTP client being worked with. + */ + private FileTransferClient ftpClient; + + /** + * Instance of filetime fixing plug-in to use. + */ + private FiletimeFixer filetimeFixer; + + /** + * Comparator to list remote directories before files. + */ + private Comparator ftpFileComparator = new Comparator() + { + /** + * {@inheritDoc} + */ + @Override + public int compare(final FTPFile file1, final FTPFile file2) + { + return (file1.isDir() && file2.isDir()) || (!file1.isDir() && !file2.isDir()) ? 0 + : (file1.isDir() && !file2.isDir() ? -1 : 1); + } + }; + + /** + * Fix filetimes recursively. + */ + private boolean recursive; + + /** + * Initializes the FTP client module. + */ + private FtpController() + { + ConfigController.getInstance().addObserver(this); + LoggerFactory.getLogger(FtpController.class).debug("Setting up FTP client..."); + this.ftpClient = new FileTransferClient(); + this.update(null, null); + this.ftpClient.setEventListener(new EventListener() + { + /** + * {@inheritDoc} + */ + @Override + public void commandSent(final String connId, final String cmd) + { + LoggerFactory.getLogger(EventListener.class).trace(" Command '{}' sent", connId, cmd); + GuiController.getInstance().appendLogEntry(cmd); + } + + /** + * {@inheritDoc} + */ + @Override + public void replyReceived(final String connId, final String reply) + { + LoggerFactory.getLogger(EventListener.class).trace(" Reply '{}' received", connId, reply); + GuiController.getInstance().appendLogEntry(reply); + } + + /** + * {@inheritDoc} + */ + @Override + public void bytesTransferred(final String connId, final String remoteFilename, final long bytes) + { + LoggerFactory.getLogger(EventListener.class).trace(" Transferred {} bytes for file '{}'", new Object[] + { + connId, bytes, remoteFilename + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void downloadStarted(final String connId, final String remoteFilename) + { + LoggerFactory.getLogger(EventListener.class).trace(" Started download for file '{}'...", connId, remoteFilename); + } + + /** + * {@inheritDoc} + */ + @Override + public void downloadCompleted(final String connId, final String remoteFilename) + { + LoggerFactory.getLogger(EventListener.class).trace(" Finished download for file '{}'", connId, remoteFilename); + } + + /** + * {@inheritDoc} + */ + @Override + public void uploadStarted(final String connId, final String remoteFilename) + { + LoggerFactory.getLogger(EventListener.class).trace(" Started upload for file '{}'...", connId, remoteFilename); + } + + /** + * {@inheritDoc} + */ + @Override + public void uploadCompleted(final String connId, final String remoteFilename) + { + LoggerFactory.getLogger(EventListener.class).trace(" Finished upload for file '{}'", connId, remoteFilename); + } + }); + //http://www.enterprisedt.com/products/edtftpj/examples/howto/monitor_transfers_and_commands/MonitorTransfersCommands.java.html + // the transfer notify interval must be greater than buffer + this.ftpClient.getAdvancedSettings().setTransferBufferSize(500); + this.ftpClient.getAdvancedSettings().setTransferNotifyInterval(1000); + } + + /** + * Returns the singleton instance of this class. + * + * @return Instance of this class + */ + public static FtpController getInstance() + { + return FtpController.ftpController; + } + + /** + * Changes current remote working directory. + * + * @param filename Directory to change to + * @throws JFixError If changing remote folder failed + */ + public void changeDir(final String filename) throws JFixError + { + try + { + this.ftpClient.changeDirectory(filename); + } + catch(final FTPException e) + { + throw new JFixError("Can't change remote directory to '" + filename + "'!", e); + } + catch(final IOException e) + { + throw new JFixError(e); + } + } + + /** + * Connects to FTP host. + * + * @throws JFixError If connecting failed + */ + public void connect() throws JFixError + { + LoggerFactory.getLogger(FtpController.class).debug("Connecting to server..."); + try + { + this.ftpClient.connect(); + } + catch(final FTPException e) + { + if(e.getReplyCode() == 530) + throw new JFixFtpAuthError("Login rejected from server!", e); + else + throw new JFixError("Can't connect to FTP host!", e); + } + catch(final IOException e) + { + throw new JFixError(e); + } + LoggerFactory.getLogger(FtpController.class).info("Connected to {}!", this.ftpClient.getRemoteHost()); + } + + /** + * Disconnects from FTP host. + * + * @throws JFixError If disconnecting failed + */ + public void disconnect() throws JFixError + { + LoggerFactory.getLogger(FtpController.class).debug("Disconnecting from server..."); + try + { + this.ftpClient.disconnect(); + } + catch(final FTPException e) + { + throw new JFixError("Can't disconnect from FTP host!", e); + } + catch(final IOException e) + { + throw new JFixError(e); + } + LoggerFactory.getLogger(FtpController.class).info("Disconnected from {}!", this.ftpClient.getRemoteHost()); + } + + /** + * Fixes filetimes of files in stated local dir with the ones in current remote working directory. + * + * @param localDir Local directory with files and folders to get filetimes from + */ + public void fixFiletimes(final File localDir) throws JFixError + { + LoggerFactory.getLogger(FtpController.class).info("Fixing filetimes for '{}'...", localDir.getName()); + for(final File curFile : localDir.listFiles()) + if(this.recursive && curFile.isDirectory()) + { + try + { + this.changeDir(curFile.getName()); + } + catch(final JFixError e) + { + e.warn(FtpController.class, true); + continue; + } + this.fixFiletimes(curFile); + this.changeDir("../"); + } + else + try + { + this.ftpClient.executeCommand(this.filetimeFixer.getCommand(curFile.getName(), curFile.lastModified())); + } + catch(final FTPException e) + { + throw new JFixError("Can't execute command!", e); + } + catch(final IOException e) + { + throw new JFixError(e); + } + LoggerFactory.getLogger(FtpController.class).info("Filetimes fixed!"); + } + + /** + * Returns sorted file list of current remote working directory. + * + * @return Remote file list + * @throws JFixError If getting remote file list failed + */ + public String[] getFileList() throws JFixError + { + try + { + //Get original file list and sort it + final FTPFile[] fileList = this.ftpClient.directoryList(); + Arrays.sort(fileList, this.ftpFileComparator); + //Convert to name list only + final LinkedList nameList = new LinkedList(); + for(final FTPFile curFile : fileList) + nameList.add(curFile.getName()); + //Add relative paths + if(!nameList.contains("..")) + nameList.addFirst(".."); + if(!nameList.contains(".")) + nameList.addFirst("."); + return nameList.toArray(new String[0]); + } + catch(final FTPException e) + { + throw new JFixError("Can't get remote directory file list!", e); + } + catch(final ParseException e) + { + throw new JFixError(e); + } + catch(final IOException e) + { + throw new JFixError(e); + } + } + + /** + * Returns if a FTP connection is currently active. + * + * @return Connection state + */ + public boolean isConnected() + { + return this.ftpClient.isConnected(); + } + + /** + * Sets host name or IP address of FTP server. + * + * @param remoteHost Name or IP address + * @throws JFixError If name or IP address cannot be set + */ + public void setHost(final String remoteHost) throws JFixError + { + try + { + this.ftpClient.setRemoteHost(remoteHost); + } + catch(final FTPException e) + { + throw new JFixError("Can't set host address '" + remoteHost + "'!", e); + } + } + + /** + * Sets the password to authenticate with the FTP server. + * + * @param password Password to use + * @throws JFixError If password cannot be set + */ + public void setPassword(final String password) throws JFixError + { + try + { + this.ftpClient.setPassword(password); + } + catch(final FTPException e) + { + throw new JFixError("Can't set password!", e); + } + } + + /** + * Sets port number for connection. + * + * @param remotePort Port to use + * @throws JFixError If port number cannot be set + */ + public void setPort(final int remotePort) throws JFixError + { + try + { + this.ftpClient.setRemotePort(remotePort); + } + catch(final FTPException e) + { + throw new JFixError("Can't set port number to " + remotePort + "!", e); + } + } + + /** + * Sets the user name to authenticate with the FTP server. + * + * @param userName User name to use + * @throws JFixError If user name cannot be set + */ + public void setUserName(final String userName) throws JFixError + { + try + { + this.ftpClient.setUserName(userName); + } + catch(final FTPException e) + { + throw new JFixError("Can't set user name '" + userName + "'!", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void update(final Observable o, final Object arg) + { + try + { + this.setHost(ConfigController.getInstance().getCfgVal("host")); + this.setPort(Integer.parseInt(ConfigController.getInstance().getCfgVal("port"))); + this.setUserName(ConfigController.getInstance().getCfgVal("user")); + this.setPassword(CryptUtils.decode(ConfigController.getInstance().getCfgVal("pass"))); + this.filetimeFixer = (FiletimeFixer) Class.forName("com.chrissyx.jfix.plugins." + ConfigController.getInstance().getCfgVal("plugIn")).newInstance(); + this.recursive = Boolean.parseBoolean(ConfigController.getInstance().getCfgVal("recursive")); + } + catch(final JFixError e) + { + e.error(FtpController.class); + } + catch(final ClassNotFoundException e) + { + new JFixError("Can't find plugin '" + ConfigController.getInstance().getCfgVal("plugIn") + "'!", e).error(FtpController.class); + } + catch(final InstantiationException e) + { + new JFixError(e).error(FtpController.class); + } + catch(final IllegalAccessException e) + { + new JFixError(e).error(FtpController.class); + } + } +} diff --git a/src/com/chrissyx/jfix/modules/GuiController.java b/src/com/chrissyx/jfix/modules/GuiController.java new file mode 100644 index 0000000..e475d24 --- /dev/null +++ b/src/com/chrissyx/jfix/modules/GuiController.java @@ -0,0 +1,329 @@ +package com.chrissyx.jfix.modules; + +import com.chrissyx.jfix.JFix; +import com.chrissyx.jfix.common.error.JFixError; +import com.chrissyx.jfix.common.error.JFixFtpAuthError; +import com.chrissyx.jfix.gui.MainView; +import com.chrissyx.jfix.gui.OptionsView; +import com.chrissyx.jfix.gui.impl.MainFrame; +import com.chrissyx.jfix.gui.impl.OptionsDialog; +import com.chrissyx.jfix.modules.util.CryptUtils; +import com.chrissyx.jfix.modules.util.FileUtils; + +import java.io.File; +import java.io.IOException; + +import javax.swing.JFrame; +import javax.swing.JOptionPane; +import javax.swing.UIManager; + +import org.slf4j.LoggerFactory; + +/** + * Creates GUIs and handles their events. + * + * @author Chrissyx + */ +public final class GuiController +{ + /** + * Singleton instance of this class. + */ + private static GuiController guiController = new GuiController(); + + /** + * The main frame to handle. + */ + private MainView mainView; + + /** + * The options frame to handle. + */ + private OptionsView optionsView; + + /** + * Current local working directory for file browsing. + */ + private File cwdLocal; + + /** + * Current remote working directory for file browsing. + */ + private String cwdRemote; + + /** + * Creates and shows main frame. + */ + private GuiController() + { + LoggerFactory.getLogger(GuiController.class).debug("Setting system L&F..."); + try + { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } + catch(final Exception e) + { + LoggerFactory.getLogger(GuiController.class).warn("Cannot set system L&F!", e); + } + LoggerFactory.getLogger(GuiController.class).debug("Creating main frame..."); + this.mainView = new MainFrame(); + this.mainView.setLocalFileList(FileUtils.getFileListing(this.cwdLocal = new File(ConfigController.getInstance().getCfgVal("localStartDir")), true)); + this.mainView.showFrame(); + this.appendLogEntry(LangController.getInstance().getString("jfix_version_x_ready", JFix.VERSION)); + this.appendLogEntry("© 2011 by Chrissyx"); + this.appendLogEntry("http://www.chrissyx.de(.vu)/"); + this.appendLogEntry("http://www.chrissyx.com/"); + } + + /** + * Returns the singleton instance of this class. + * + * @return Instance of this class + */ + public static GuiController getInstance() + { + return GuiController.guiController; + } + + /** + * Displays given log message in console text area. + * + * @param logMessage Message to log + */ + public void appendLogEntry(final String logMessage) + { + this.mainView.appendLogEntry(logMessage); + } + + /** + * Changes file list of selected local directory. + * + * @param filename Name of local directory + */ + public void onChangeLocalDir(final String filename) + { + LoggerFactory.getLogger(GuiController.class).debug("Browsing locally to '{}' from '{}'...", filename, this.cwdLocal); + try + { + final File newLocation = filename.contentEquals("..") ? this.cwdLocal.getParentFile() : new File(this.cwdLocal.getCanonicalPath().concat(File.separator).concat(filename)); + if(newLocation.isDirectory()) + this.cwdLocal = newLocation; + } + catch(final IOException e) + { + new JFixError(e).error(GuiController.class); + } + this.mainView.setLocalFileList(FileUtils.getFileListing(this.cwdLocal, true)); + } + + /** + * Changes file list of selected remote directory via {@link FtpController}. + * + * @param filename Name of remote directory + */ + public void onChangeRemoteDir(final String filename) + { + LoggerFactory.getLogger(GuiController.class).debug("Browsing remotely to '{}' from '{}'...", filename, this.cwdRemote); + if(!FtpController.getInstance().isConnected()) + return; + final String oldRemoteDir = this.cwdRemote; //Back up current dir in case of error (file selected, ...) + if(!filename.contentEquals(".")) + this.cwdRemote = filename.contentEquals("..") ? this.getRemoteParentDir() : this.cwdRemote.concat(filename).concat("/"); + try + { + FtpController.getInstance().changeDir(this.cwdRemote); + this.mainView.setRemoteFileList(FtpController.getInstance().getFileList()); + } + catch(final JFixError e) + { + this.cwdRemote = oldRemoteDir; + e.error(FtpController.class); + } + } + + /** + * Returns the parent remote working directory from the current one. + * + * @return Remote working directory path down to parent of current one + */ + private String getRemoteParentDir() + { + if(this.cwdRemote.length() == 1) + return this.cwdRemote; + final StringBuilder stringBuilder = new StringBuilder(); + final String[] remoteDirs = this.cwdRemote.split("/"); + for(int i = 0; i < remoteDirs.length - 1; i++) + stringBuilder.append(remoteDirs[i]).append("/"); + return stringBuilder.toString(); + } + + /** + * Closes all frames and exits application. + */ + public void onClose() + { + LoggerFactory.getLogger(GuiController.class).info("Exiting JFix..."); + this.onDisconnect(); + this.mainView.closeFrame(); + System.exit(0); + } + + /** + * Connects to FTP server via {@link FtpController} and changes to preset remote directory. + * + * @return State of connect / change dir operation + */ + public boolean onConnect() + { + this.mainView.clearLogConsole(); + try + { + FtpController.getInstance().connect(); + FtpController.getInstance().changeDir(this.cwdRemote = ConfigController.getInstance().getCfgVal("remoteStartDir")); + this.mainView.setRemoteFileList(FtpController.getInstance().getFileList()); + return true; + } + catch(final JFixFtpAuthError e) + { + e.warn(FtpController.class, false); + this.showWarningDialog(LangController.getInstance().getString("text_login_failed"), LangController.getInstance().getString("title_login_failed")); + } + catch(final JFixError e) + { + e.error(FtpController.class); + } + this.onDisconnect(); + return false; + } + + /** + * Disconnects (if connected) from FTP server via {@link FtpController}. + * + * @return State of disconnect operation + */ + public boolean onDisconnect() + { + if(FtpController.getInstance().isConnected()) + try + { + FtpController.getInstance().disconnect(); + return true; + } + catch(final JFixError e) + { + e.error(FtpController.class); + } + return false; + } + + /** + * Starts synchronizing remote filetimes with local ones via {@link FtpController}. + */ + public void onFixFiletimes() + { + if(FtpController.getInstance().isConnected()) + try + { + FtpController.getInstance().fixFiletimes(this.cwdLocal); + } + catch(final JFixError e) + { + e.error(FtpController.class); + } + else + this.showWarningDialog(LangController.getInstance().getString("text_connect_first"), LangController.getInstance().getString("title_connect_first")); + } + + /** + * Checks and saves settings to file. + */ + public void onSaveOptions() + { + if(!this.optionsView.getOptionRemoteStartDir().endsWith("/")) + { + this.showWarningDialog(LangController.getInstance().getString("text_trailing_slash_needed"), LangController.getInstance().getString("title_trailing_slash_needed")); + return; + } + ConfigController.getInstance().setCfgVal("host", this.optionsView.getOptionHost()); + ConfigController.getInstance().setCfgVal("port", this.optionsView.getOptionPort()); + ConfigController.getInstance().setCfgVal("user", this.optionsView.getOptionUser()); + ConfigController.getInstance().setCfgVal("pass", CryptUtils.encode(this.optionsView.getOptionPass())); + ConfigController.getInstance().setCfgVal("language", this.optionsView.getOptionLanguage()); + ConfigController.getInstance().setCfgVal("localStartDir", this.optionsView.getOptionLocalStartDir()); + ConfigController.getInstance().setCfgVal("remoteStartDir", this.optionsView.getOptionRemoteStartDir()); + ConfigController.getInstance().setCfgVal("plugIn", this.optionsView.getOptionPlugIn()); + ConfigController.getInstance().setCfgVal("recursive", Boolean.toString(this.optionsView.getOptionRecursive())); + try + { + ConfigController.getInstance().save(); + this.optionsView.closeFrame(); + } + catch(final JFixError e) + { + e.error(ConfigController.class); + } + } + + /** + * Shows the options frame. + */ + public void onShowOptions() + { + if(this.optionsView == null) + { + LoggerFactory.getLogger(GuiController.class).debug("Creating options frame..."); + this.optionsView = new OptionsDialog((JFrame) this.mainView, true); + } +// ((JFrame) this.optionsView).addWindowListener(new java.awt.event.WindowAdapter() +// { +// @Override +// public void windowClosing(final java.awt.event.WindowEvent e) +// { +// System.exit(0); +// } +// }); + this.optionsView.showFrame(); + } + + /** + * Displays given message in an error dialog. + * + * @param message Message text to display + */ + public void showErrorDialog(final String message) + { + this.showErrorDialog(message, LangController.getInstance().getString("error")); + } + + /** + * Displays given message and title in an error dialog. + * + * @param message Message text to display + * @param title Title of error dialog + */ + private void showErrorDialog(final String message, final String title) + { + JOptionPane.showMessageDialog((JFrame) this.mainView, message, title, JOptionPane.ERROR_MESSAGE); + } + + /** + * Displays given message in a warning dialog. + * + * @param message Message text to display + */ + public void showWarningDialog(final String message) + { + this.showWarningDialog(message, LangController.getInstance().getString("warning")); + } + + /** + * Displays given message and title in a warning dialog. + * + * @param message Message text to display + * @param title Title of warning dialog + */ + public void showWarningDialog(final String message, final String title) + { + JOptionPane.showMessageDialog((JFrame) this.mainView, message, title, JOptionPane.WARNING_MESSAGE); + } +} diff --git a/src/com/chrissyx/jfix/modules/LangController.java b/src/com/chrissyx/jfix/modules/LangController.java new file mode 100644 index 0000000..02a9c89 --- /dev/null +++ b/src/com/chrissyx/jfix/modules/LangController.java @@ -0,0 +1,151 @@ +package com.chrissyx.jfix.modules; + +import com.chrissyx.jfix.modules.util.FileUtils; +import com.chrissyx.jfix.modules.util.ResourceUtils; + +import java.text.MessageFormat; +import java.util.LinkedList; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.Observable; +import java.util.Observer; +import java.util.ResourceBundle; + +import org.slf4j.LoggerFactory; + +/** + * Manages translation and formatting of language strings. + * + * @author Chrissyx + * @since 0.4 + */ +public final class LangController implements Observer +{ + /** + * Singleton instance of this class. + */ + private static LangController langController = new LangController(); + + /** + * Handler / cache for localized strings. + */ + private ResourceBundle resourceBundle; + + /** + * Name of base resource file. + */ + private final String resourceName = ResourceUtils.getPath("jfix-lang"); + + /** + * Loads localization based on detected user's preference. + */ + private LangController() + { + ConfigController.getInstance().addObserver(this); + try + { + this.resourceBundle = ResourceBundle.getBundle(this.resourceName, new Locale(ConfigController.getInstance().getCfgVal("language"))); + } + catch(final MissingResourceException e) + { + LoggerFactory.getLogger(LangController.class).error("No localization file found!", e); + } + LoggerFactory.getLogger(LangController.class).info("Localization for '{}' loaded", this.resourceBundle.getLocale().getLanguage()); + } + + /** + * Returns the singleton instance of this class. + * + * @return Instance of this class + */ + public static LangController getInstance() + { + return LangController.langController; + } + + /** + * Returns list of supported locales. + * + * @return Locales usable for this controller + */ + public String[] getLocales() + { + final LinkedList localeList = new LinkedList(); + for(final String curLocale : FileUtils.getPackageListing("resources", ".properties")) + localeList.add(curLocale.replace("jfix-lang_", "")); //.replaceFirst("_", "")); + return localeList.toArray(new String[0]); + } + + /** + * Returns a translated language string for stated key. + * + * @param key Identifier of translated string + * @return Localized string or the key itself if identifier was not found + */ + public String getString(final String key) + { + LoggerFactory.getLogger(LangController.class).debug("Returning string for key '{}'...", key); + try + { + return this.resourceBundle.getString(key); + } + catch(final MissingResourceException e) + { + LoggerFactory.getLogger(LangController.class).warn("No localization for key '{}' found!", key); + } + return key; + } + + /** + * Returns a parametrized translated string for stated key and replaces given arguments. + * The string has to define a placeholder with {@code {argumentIndex[,formatType[,formatStyle]]}}, + * e.g. {@code {0,number,integer}} to format a number as an integer and insert it at position 0. + * + * @param key Identifier of translated string + * @param args Argument(s) to insert into translated string at predefined positions + * @return Localized string or the key itself if identifier was not found + */ + public String getString(final String key, final Object... args) + { + try + { + return MessageFormat.format(this.getString(key), args); + } + catch(final IllegalArgumentException e) + { + LoggerFactory.getLogger(LangController.class).warn("Invalid pattern in translated string or wrong type of arguments for key '{}'!", key); + } + return key; + } + + /** + * Sets new locale to use for string translations. + * + * @param newLocale New locale + */ + public void setLocale(final Locale newLocale) + { + ResourceBundle.clearCache(); + try + { + this.resourceBundle = ResourceBundle.getBundle(this.resourceName, newLocale); + if(newLocale.getLanguage().equals(this.resourceBundle.getLocale().getLanguage())) + LoggerFactory.getLogger(LangController.class).debug("New locale set to '{}'", newLocale.getLanguage()); + else + LoggerFactory.getLogger(LangController.class).warn("No localization file for '{}' found!", newLocale.getLanguage()); + } + catch(final MissingResourceException e) + { + LoggerFactory.getLogger(LangController.class).error("No localization file found!", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void update(final Observable o, final Object arg) + { + this.setLocale(new Locale(ConfigController.getInstance().getCfgVal("language"))); + } +} diff --git a/src/com/chrissyx/jfix/modules/package-info.java b/src/com/chrissyx/jfix/modules/package-info.java new file mode 100644 index 0000000..d298a19 --- /dev/null +++ b/src/com/chrissyx/jfix/modules/package-info.java @@ -0,0 +1,7 @@ +/** + * Contains all the application logic separated in modules. + * + * @author Chrissyx + * @since 0.2 + */ +package com.chrissyx.jfix.modules; diff --git a/src/com/chrissyx/jfix/modules/util/CryptUtils.java b/src/com/chrissyx/jfix/modules/util/CryptUtils.java new file mode 100644 index 0000000..a5e2b77 --- /dev/null +++ b/src/com/chrissyx/jfix/modules/util/CryptUtils.java @@ -0,0 +1,40 @@ +package com.chrissyx.jfix.modules.util; + +import org.apache.commons.codec.binary.Base64; + +/** + * Utils for encryption and decryption. + * + * @author Chrissyx + */ +public class CryptUtils +{ + /** + * Hidden constructor to prevent instances of this class. + */ + private CryptUtils() + { + } + + /** + * Decodes an Base64 encoded string. + * + * @param string Base64 string to decode + * @return Decoded string + */ + public static String decode(final String string) + { + return new String(Base64.decodeBase64(string)); + } + + /** + * Encodes a string with Base64. + * + * @param string String to encode + * @return Base64 encoded string + */ + public static String encode(final String string) + { + return Base64.encodeBase64String(string.getBytes()); + } +} diff --git a/src/com/chrissyx/jfix/modules/util/FileUtils.java b/src/com/chrissyx/jfix/modules/util/FileUtils.java new file mode 100644 index 0000000..b026fde --- /dev/null +++ b/src/com/chrissyx/jfix/modules/util/FileUtils.java @@ -0,0 +1,99 @@ +package com.chrissyx.jfix.modules.util; + +import com.chrissyx.jfix.common.error.JFixError; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +/** + * Utils for file and directory operations. + * + * @author Chrissyx + */ +public class FileUtils +{ + /** + * Comparator to list directories before files. + */ + private static final Comparator FILE_COMPARATOR = new Comparator() + { + /** + * {@inheritDoc} + */ + @Override + public int compare(final File file1, final File file2) + { + return (file1.isDirectory() && file2.isDirectory()) || (!file1.isDirectory() && !file2.isDirectory()) ? 0 + : (file1.isDirectory() && !file2.isDirectory() ? -1 : 1); + } + }; + + /** + * Hidden constructor to prevent instances of this class. + */ + private FileUtils() + { + } + + /** + * Returns listing with all found files from stated package and given file ending of this JAR. + * + * @param packageName Name of package to look up + * @param fileEnding File ending to consider + * @param exclude Names of files to exclude from search result + * @return Found files from given package with matching file ending + */ + public static String[] getPackageListing(final String packageName, final String fileEnding, final String... exclude) + { + final LinkedList classList = new LinkedList(); + JarEntry curJarEntry; + String curName; + try + { + final JarInputStream jarInputStream = new JarInputStream(new FileInputStream("JFix.jar")); + while((curJarEntry = jarInputStream.getNextJarEntry()) != null) + if((curName = curJarEntry.getName()).contains(packageName) && curName.endsWith(fileEnding)) + classList.add(curName.substring(curName.lastIndexOf("/") + 1, curName.lastIndexOf("."))); + } + catch(final FileNotFoundException e) + { + new JFixError("JFix JAR for package listing not found!", e).error(FileUtils.class); + } + catch(final IOException e) + { + new JFixError(e).error(FileUtils.class); + } + if(exclude != null) + classList.removeAll(Arrays.asList(exclude)); + return classList.toArray(new String[0]); + } + + /** + * Returns a file listing from stated directory. ".." is added to address the super folder. + * + * @param directory Directory to list files from + * @param sort List directories before files + * @return File listing with super folder notation + */ + public static String[] getFileListing(final File directory, final boolean sort) + { + if(!directory.isDirectory()) + throw new IllegalArgumentException(directory + " is not a directory to list files from!"); + final File[] fileList = directory.listFiles(); + if(sort) + Arrays.sort(fileList, FileUtils.FILE_COMPARATOR); + final LinkedList filenameList = new LinkedList(); + if(directory.getParentFile() != null) + filenameList.add(".."); + for(final File curFile : fileList) + filenameList.add(curFile.getName()); + return filenameList.toArray(new String[0]); + } +} diff --git a/src/com/chrissyx/jfix/modules/util/GuiUtils.java b/src/com/chrissyx/jfix/modules/util/GuiUtils.java new file mode 100644 index 0000000..740b88c --- /dev/null +++ b/src/com/chrissyx/jfix/modules/util/GuiUtils.java @@ -0,0 +1,42 @@ +package com.chrissyx.jfix.modules.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Some helping methods for GUI classes. + * + * @author Chrissyx + * @since 0.14 + */ +public class GuiUtils +{ + /** + * Hidden constructor to prevent instances of this class. + */ + private GuiUtils() + { + } + + /** + * Returns user-friendly plug-in names of given class names, e.g. "SiteUtime" converts to "SITE UTIME". + * + * @param classList List with plug-in class names + * @return Display names of provided list + */ + public static String[] getPlugInNames(final String[] classList) + { + String curPlugInName; + Matcher curMatcher; + for(int i = 0; i < classList.length; i++) + { + curPlugInName = ""; + curMatcher = Pattern.compile("([A-Z0-9][a-z0-9]+)").matcher(classList[i]); + while(curMatcher.find()) + curPlugInName += curMatcher.group().toUpperCase() + " "; + if(!curPlugInName.isEmpty()) + classList[i] = curPlugInName.trim(); + } + return classList; + } +} diff --git a/src/com/chrissyx/jfix/modules/util/ResourceUtils.java b/src/com/chrissyx/jfix/modules/util/ResourceUtils.java new file mode 100644 index 0000000..16cafeb --- /dev/null +++ b/src/com/chrissyx/jfix/modules/util/ResourceUtils.java @@ -0,0 +1,57 @@ +package com.chrissyx.jfix.modules.util; + +import java.awt.image.BufferedImage; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.slf4j.LoggerFactory; + +/** + * Utilities for resources. + * + * @author Chrissyx + */ +public class ResourceUtils +{ + /** + * Hidden constructor to prevent instances of this class. + */ + private ResourceUtils() + { + } + + /** + * Loads and returns image from the resource directory. + * + * @param filename Name of image file + * @return Loaded image + */ + public static BufferedImage getImage(final String filename) + { + try + { + return ImageIO.read(Thread.currentThread().getContextClassLoader().getResourceAsStream(ResourceUtils.getPath("icon.png"))); + } + catch(final IllegalArgumentException e) + { + LoggerFactory.getLogger(ResourceUtils.class).warn("Cannot find application icon!", e); + } + catch(final IOException e) + { + LoggerFactory.getLogger(ResourceUtils.class).warn("Cannot get application icon!", e); + } + return null; + } + + /** + * Returns path to a file in the resource directory. + * + * @param filename Name of file in resource folder + * @return Path to file in resource folder + */ + public static String getPath(final String filename) + { + return "com/chrissyx/jfix/resources/" + filename; + } +} diff --git a/src/com/chrissyx/jfix/modules/util/package-info.java b/src/com/chrissyx/jfix/modules/util/package-info.java new file mode 100644 index 0000000..20b7159 --- /dev/null +++ b/src/com/chrissyx/jfix/modules/util/package-info.java @@ -0,0 +1,6 @@ +/** + * Contains various utilities for the controller modules from the parent package. + * + * @author Chrissyx + */ +package com.chrissyx.jfix.modules.util; diff --git a/src/com/chrissyx/jfix/package-info.java b/src/com/chrissyx/jfix/package-info.java new file mode 100644 index 0000000..186599d --- /dev/null +++ b/src/com/chrissyx/jfix/package-info.java @@ -0,0 +1,6 @@ +/** + * The main package contains just the application entry point. + * + * @author Chrissyx + */ +package com.chrissyx.jfix; diff --git a/src/com/chrissyx/jfix/package.html b/src/com/chrissyx/jfix/package.html deleted file mode 100644 index 8552759..0000000 --- a/src/com/chrissyx/jfix/package.html +++ /dev/null @@ -1,9 +0,0 @@ - - - -com.chrissyx.jfix - - -Contains the main JFix classes. - - \ No newline at end of file diff --git a/src/com/chrissyx/jfix/plugins/FiletimeFixer.java b/src/com/chrissyx/jfix/plugins/FiletimeFixer.java new file mode 100644 index 0000000..7e2f7d2 --- /dev/null +++ b/src/com/chrissyx/jfix/plugins/FiletimeFixer.java @@ -0,0 +1,36 @@ +package com.chrissyx.jfix.plugins; + +import java.text.SimpleDateFormat; + +/** + * General template for implementing plug-ins to specify individual FTP command patterns. + * + * @author Chrissyx + * @since 0.9 + */ +public abstract class FiletimeFixer +{ + /** + * Date formatter to prepare the filetimes. + */ + protected final SimpleDateFormat dateFormatter = new SimpleDateFormat(); + + /** + * Applies date pattern to the provided internal date formatter. + * + * @param datePattern Pattern to format FTP command specific date + */ + protected FiletimeFixer(final String datePattern) + { + this.dateFormatter.applyPattern(datePattern); + } + + /** + * Returns ready-to-use FTP command to fix remote filetime of stated local file with given timestamp. + * + * @param filename Name of local and remote file to fix its filetime + * @param timestamp Timestamp to apply + * @return FTP command for execution + */ + public abstract String getCommand(final String filename, final long timestamp); +} diff --git a/src/com/chrissyx/jfix/plugins/SiteUtime.java b/src/com/chrissyx/jfix/plugins/SiteUtime.java new file mode 100644 index 0000000..66364c5 --- /dev/null +++ b/src/com/chrissyx/jfix/plugins/SiteUtime.java @@ -0,0 +1,30 @@ +package com.chrissyx.jfix.plugins; + +import java.util.Date; + +/** + * Filetime fixing plug-in for {@code SITE UTIME} command. + * + * @author Chrissyx + * @since 0.9 + */ +public class SiteUtime extends FiletimeFixer +{ + /** + * Sets date pattern. + */ + public SiteUtime() + { + super("yyyyMMddHHmmss"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getCommand(final String filename, final long timestamp) + { + final String curTime = this.dateFormatter.format(new Date(timestamp)); + return "SITE UTIME " + filename + " " + curTime + " " + curTime + " " + curTime + " UTC"; + } +} diff --git a/src/com/chrissyx/jfix/plugins/package-info.java b/src/com/chrissyx/jfix/plugins/package-info.java new file mode 100644 index 0000000..d2dd695 --- /dev/null +++ b/src/com/chrissyx/jfix/plugins/package-info.java @@ -0,0 +1,6 @@ +/** + * All filetime fixing FTP command plug-ins are stored here. Each plug-in inherits from {@link com.chrissyx.jfix.plugins.FiletimeFixer}. + * + * @author Chrissyx + */ +package com.chrissyx.jfix.plugins; diff --git a/src/com/chrissyx/jfix/resources/icon.png b/src/com/chrissyx/jfix/resources/icon.png new file mode 100644 index 0000000..4f489f4 Binary files /dev/null and b/src/com/chrissyx/jfix/resources/icon.png differ diff --git a/src/com/chrissyx/jfix/resources/jfix-lang_de_DE.properties b/src/com/chrissyx/jfix/resources/jfix-lang_de_DE.properties new file mode 100644 index 0000000..31fd870 --- /dev/null +++ b/src/com/chrissyx/jfix/resources/jfix-lang_de_DE.properties @@ -0,0 +1,32 @@ +connect = Verbinden +fix_it = Fix it! +options = Optionen +exit = Beenden +jfix_version_x_ready = JFix V{0} bereit + +error = Fehler +warning = Warnung + +title_login_failed = Login fehlgeschlagen +text_login_failed = Server verweigerte Anmeldung - Falsches Passwort? +title_connect_first = Fehlende Verbindung +text_connect_first = Bitte zuerst mit dem FTP-Server verbinden. + +# Options +ftp_connection = FTP-Verbindung +host = Host +port_number = Portnummer +user_name = Benutzername +password = Passwort + +general = Allgemein +local_start_directory = Lokaler Anfangsordner +dots = ... +remote_start_directory = Externer Anfangsordner +plug_in_to_use = Verwendetes Plug-in +fix_filetimes_recursively = Dateizeiten rekursiv fixen +language_after_restart = Sprache (nach Neustart) +save = Speichern + +title_trailing_slash_needed = Schr\u00e4gstrich fehlt +text_trailing_slash_needed = Der externe Anfangsordner muss mit einem Schr\u00e4gstrich enden. \ No newline at end of file diff --git a/src/com/chrissyx/jfix/resources/jfix-lang_en_US.properties b/src/com/chrissyx/jfix/resources/jfix-lang_en_US.properties new file mode 100644 index 0000000..40f66df --- /dev/null +++ b/src/com/chrissyx/jfix/resources/jfix-lang_en_US.properties @@ -0,0 +1,32 @@ +connect = Connect +fix_it = Fix it! +options = Options +exit = Exit +jfix_version_x_ready = JFix V{0} ready + +error = Error +warning = Warning + +title_login_failed = Login failed +text_login_failed = Server rejected login - wrong password? +title_connect_first = Missing connection +text_connect_first = Please connect to FTP server first. + +# Options +ftp_connection = FTP connection +host = Host +port_number = Port number +user_name = User name +password = Password + +general = General +local_start_directory = Local start directory +dots = ... +remote_start_directory = Remote start directory +plug_in_to_use = Plug-in to use +fix_filetimes_recursively = Fix filetimes recursively +language_after_restart = Language (after restart) +save = Save + +title_trailing_slash_needed = Slash needed +text_trailing_slash_needed = The remote start directory has to end with a slash. \ No newline at end of file diff --git a/src/com/chrissyx/jfix/resources/package-info.java b/src/com/chrissyx/jfix/resources/package-info.java new file mode 100644 index 0000000..829df86 --- /dev/null +++ b/src/com/chrissyx/jfix/resources/package-info.java @@ -0,0 +1,6 @@ +/** + * Contains various resources, e.g. images and translation files. + * + * @author Chrissyx + */ +package com.chrissyx.jfix.resources; diff --git a/test/.gitkeep b/test/.gitkeep deleted file mode 100644 index e69de29..0000000