diff --git a/cli/pom.xml b/cli/pom.xml
index 980eb03..d74c730 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -1,122 +1,122 @@
- 4.0.0
+ 4.0.0
-
- com.sothr.imagetools
- parent
- 1.0.1
- ../parent
-
+
+ com.sothr.imagetools
+ parent
+ 1.0.1
+ ../parent
+
- cli
- 0.1.1
- jar
+ cli
+ 0.1.1
+ jar
- ImageTools-CLI
- The Command Line Interface for Image-Tools
- http://imagetools.sothr.com
-
- Sothr Software
-
+ ImageTools-CLI
+ The Command Line Interface for Image-Tools
+ http://imagetools.sothr.com
+
+ Sothr Software
+
-
-
- com.sothr.imagetools
- engine
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
- ch.qos.logback
- logback-access
-
-
- org.slf4j
- slf4j-api
-
-
- org.clapper
- grizzled-slf4j_${scala.binary.version}
-
-
- org.scala-lang
- scala-library
-
-
+
+
+ com.sothr.imagetools
+ engine
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ ch.qos.logback
+ logback-access
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.clapper
+ grizzled-slf4j_${scala.binary.version}
+
+
+ org.scala-lang
+ scala-library
+
+
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 2.4
-
-
- package
-
- jar
-
-
-
-
- true
- lib/
- com.sothr.imagetools.cli.AppCLI
-
-
-
- ${project.build.directory}/release
-
-
-
-
-
-
- maven-antrun-plugin
- 1.4
-
-
- prepare
- process-resources
-
-
-
-
-
-
-
-
-
- run
-
-
-
- package
- package
-
-
-
-
-
-
-
- run
-
-
-
-
-
-
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.4
+
+
+ package
+
+ jar
+
+
+
+
+ true
+ lib/
+ com.sothr.imagetools.cli.AppCLI
+
+
+
+ ${project.build.directory}/release
+
+
+
+
+
+
+ maven-antrun-plugin
+ 1.4
+
+
+ prepare
+ process-resources
+
+
+
+
+
+
+
+
+
+ run
+
+
+
+ package
+ package
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+
diff --git a/cli/src/includes/logback.xml b/cli/src/includes/logback.xml
index 077b4d1..845280a 100644
--- a/cli/src/includes/logback.xml
+++ b/cli/src/includes/logback.xml
@@ -1,75 +1,75 @@
-
-
-
-
-
- false
- [%date{HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- INFO
-
-
-
-
- ImageTools.debug
-
- false
- [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- DEBUG
-
-
- 1
- ImageTools.debug.%i
-
-
- 5MB
-
-
-
-
- ImageTools.info
-
- false
- [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- INFO
-
-
- 1
- ImageTools.info.%i
-
-
- 500KB
-
-
-
-
- ImageTools.err
-
- false
- [%.16thread] [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- ERROR
-
-
- 1
- ImageTools.err.%i
-
-
- 500KB
-
-
-
-
-
-
-
-
+
+
+
+
+
+ false
+ [%date{HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ INFO
+
+
+
+
+ ImageTools.debug
+
+ false
+ [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ DEBUG
+
+
+ 1
+ ImageTools.debug.%i
+
+
+ 5MB
+
+
+
+
+ ImageTools.info
+
+ false
+ [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ INFO
+
+
+ 1
+ ImageTools.info.%i
+
+
+ 500KB
+
+
+
+
+ ImageTools.err
+
+ false
+ [%.16thread] [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ ERROR
+
+
+ 1
+ ImageTools.err.%i
+
+
+ 500KB
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/cli/src/main/java/com/sothr/imagetools/cli/AppCLI.java b/cli/src/main/java/com/sothr/imagetools/cli/AppCLI.java
index 9275ec4..aa18a4e 100644
--- a/cli/src/main/java/com/sothr/imagetools/cli/AppCLI.java
+++ b/cli/src/main/java/com/sothr/imagetools/cli/AppCLI.java
@@ -8,7 +8,13 @@ import com.sothr.imagetools.engine.CLIEngineListener;
import com.sothr.imagetools.engine.ConcurrentEngine;
import com.sothr.imagetools.engine.Engine;
import com.sothr.imagetools.engine.image.SimilarImages;
-import org.apache.commons.cli.*;
+import org.apache.commons.cli.BasicParser;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.collection.immutable.List;
@@ -18,101 +24,101 @@ import scala.collection.immutable.List;
*/
class AppCLI {
- private static final String HEADER = "Process images and search for duplicates and similar images heuristically";
- private static final String FOOTER = "Please report issues to...";
- private static Logger logger;
+ private static final String HEADER = "Process images and search for duplicates and similar images heuristically";
+ private static final String FOOTER = "Please report issues to...";
+ private static Logger logger;
- public static void main(String[] args) {
- try {
- Options options = getOptions();
- CommandLineParser parser = new BasicParser();
- CommandLine cmd = parser.parse(options, args);
- if (cmd.hasOption('h') || cmd.getOptions().length < 1 || cmd.getArgs().length > 0) {
- HelpFormatter formatter = new HelpFormatter();
- formatter.printHelp("Image-Tools CLI", HEADER, options, FOOTER, true);
- } else {
- AppConfig.configureApp();
- logger = LoggerFactory.getLogger(AppCLI.class);
- logger.info("Started Image Tools CLI");
- process(cmd);
- AppConfig.shutdown();
- }
- System.exit(0);
- } catch (Exception ex) {
- logger.error("Unhandled exception in AppCLI", ex);
- }
- }
+ public static void main(String[] args) {
+ try {
+ Options options = getOptions();
+ CommandLineParser parser = new BasicParser();
+ CommandLine cmd = parser.parse(options, args);
+ if (cmd.hasOption('h') || cmd.getOptions().length < 1 || cmd.getArgs().length > 0) {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("Image-Tools CLI", HEADER, options, FOOTER, true);
+ } else {
+ AppConfig.configureApp();
+ logger = LoggerFactory.getLogger(AppCLI.class);
+ logger.info("Started Image Tools CLI");
+ process(cmd);
+ AppConfig.shutdown();
+ }
+ System.exit(0);
+ } catch (Exception ex) {
+ logger.error("Unhandled exception in AppCLI", ex);
+ }
+ }
- private static Options getOptions() {
- //scan a list of directories
- Options options = new Options();
+ private static Options getOptions() {
+ //scan a list of directories
+ Options options = new Options();
- //show help
- Option helpOption = OptionBuilder.create('h');
- helpOption.setLongOpt("help");
- helpOption.setDescription("Display this help dialog");
- options.addOption(helpOption);
+ //show help
+ Option helpOption = OptionBuilder.create('h');
+ helpOption.setLongOpt("help");
+ helpOption.setDescription("Display this help dialog");
+ options.addOption(helpOption);
- //scan directories
- Option scanOption = OptionBuilder.create('s');
- scanOption.setLongOpt("scan");
- scanOption.setDescription("Scan directories for a list of similar images");
- scanOption.setArgs(1);
- scanOption.setArgName("DIRECTORY");
- options.addOption(scanOption);
+ //scan directories
+ Option scanOption = OptionBuilder.create('s');
+ scanOption.setLongOpt("scan");
+ scanOption.setDescription("Scan directories for a list of similar images");
+ scanOption.setArgs(1);
+ scanOption.setArgName("DIRECTORY");
+ options.addOption(scanOption);
- //scan directories in a recursive manner
- Option recursiveOption = OptionBuilder.create('r');
- recursiveOption.setLongOpt("recursive");
- recursiveOption.setDescription("Scan directories recursively");
- options.addOption(recursiveOption);
+ //scan directories in a recursive manner
+ Option recursiveOption = OptionBuilder.create('r');
+ recursiveOption.setLongOpt("recursive");
+ recursiveOption.setDescription("Scan directories recursively");
+ options.addOption(recursiveOption);
- //depth limit
- Option depthOption = OptionBuilder.create('d');
- depthOption.setLongOpt("depth");
- depthOption.setDescription("Limit the maximum depth of the recursive search");
- depthOption.setArgs(1);
- depthOption.setArgName("INTEGER");
- options.addOption(depthOption);
- return options;
- }
+ //depth limit
+ Option depthOption = OptionBuilder.create('d');
+ depthOption.setLongOpt("depth");
+ depthOption.setDescription("Limit the maximum depth of the recursive search");
+ depthOption.setArgs(1);
+ depthOption.setArgName("INTEGER");
+ options.addOption(depthOption);
+ return options;
+ }
- private static void process(CommandLine cmd) {
- //scan a comma separated list of paths to search for image similarities
- try {
- Engine engine = new ConcurrentEngine();
+ private static void process(CommandLine cmd) {
+ //scan a comma separated list of paths to search for image similarities
+ try {
+ Engine engine = new ConcurrentEngine();
- //create the listeners that will be passed onto the actors
- ActorSystem system = AppConfig.getAppActorSystem();
- Props cliListenerProps = Props.create(CLIEngineListener.class);
- ActorRef cliListener = system.actorOf(cliListenerProps);
+ //create the listeners that will be passed onto the actors
+ ActorSystem system = AppConfig.getAppActorSystem();
+ Props cliListenerProps = Props.create(CLIEngineListener.class);
+ ActorRef cliListener = system.actorOf(cliListenerProps);
- //set the listeners
- engine.setProcessedListener(cliListener);
- engine.setSimilarityListener(cliListener);
+ //set the listeners
+ engine.setProcessedListener(cliListener);
+ engine.setSimilarityListener(cliListener);
- if (cmd.hasOption('s')) {
- Boolean recursive = false;
- Integer recursiveDepth = 500;
- if (cmd.hasOption('r')) {
- recursive = true;
- }
- if (cmd.hasOption('d')) {
- recursiveDepth = Integer.parseInt(cmd.getOptionValue('d'));
- }
- String scanList = cmd.getOptionValue('s');
- String[] paths = scanList.split(",");
- for (String path : paths) {
- List similarImages = engine.getSimilarImagesForDirectory(path, recursive, recursiveDepth);
- for (int index = 0; index < similarImages.length(); index++) {
- SimilarImages similar = similarImages.apply(index);
- System.out.println(similar.toString());
- }
- }
- }
- } catch (Exception ex) {
- throw new IllegalArgumentException("One or more arguments could not be parsed correctly", ex);
- }
- }
-}
+ if (cmd.hasOption('s')) {
+ Boolean recursive = false;
+ Integer recursiveDepth = 500;
+ if (cmd.hasOption('r')) {
+ recursive = true;
+ }
+ if (cmd.hasOption('d')) {
+ recursiveDepth = Integer.parseInt(cmd.getOptionValue('d'));
+ }
+ String scanList = cmd.getOptionValue('s');
+ String[] paths = scanList.split(",");
+ for (String path : paths) {
+ List similarImages = engine.getSimilarImagesForDirectory(path, recursive, recursiveDepth);
+ for (int index = 0; index < similarImages.length(); index++) {
+ SimilarImages similar = similarImages.apply(index);
+ System.out.println(similar.toString());
+ }
+ }
+ }
+ } catch (Exception ex) {
+ throw new IllegalArgumentException("One or more arguments could not be parsed correctly", ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/pom.xml b/engine/pom.xml
index 4a88187..4848cd5 100644
--- a/engine/pom.xml
+++ b/engine/pom.xml
@@ -1,223 +1,223 @@
- 4.0.0
+ 4.0.0
-
- com.sothr.imagetools
- parent
- 1.0.1
- ../parent
-
+
+ com.sothr.imagetools
+ parent
+ 1.0.1
+ ../parent
+
- engine
- 0.1.3
- jar
- ImageTools-Engine
- An image collection management utility
- http://imagetools.sothr.com
-
- Sothr Software
-
+ engine
+ 0.1.3
+ jar
+ ImageTools-Engine
+ An image collection management utility
+ http://imagetools.sothr.com
+
+ Sothr Software
+
-
-
- com.sothr.imagetools
- hash
-
-
- junit
- junit
- test
-
-
- org.scalatest
- scalatest_${scala.binary.version}
- test
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
- ch.qos.logback
- logback-access
-
-
- org.slf4j
- slf4j-api
-
-
- org.clapper
- grizzled-slf4j_${scala.binary.version}
-
-
- org.scala-lang
- scala-library
-
-
- com.jsuereth
- scala-arm_${scala.binary.version}
-
-
- net.coobird
- thumbnailator
-
-
- com.typesafe
- config
-
-
- net.sourceforge.jtransforms
- jtransforms
-
-
- commons-cli
- commons-cli
-
-
- commons-codec
- commons-codec
-
-
- javax.transaction
- jta
-
-
- net.sf.ehcache
- ehcache
-
-
- com.typesafe.akka
- akka-actor_${scala.binary.version}
-
-
- com.typesafe.akka
- akka-slf4j_${scala.binary.version}
-
-
- com.h2database
- h2
-
-
- org.hibernate
- hibernate-core
-
-
- org.hibernate
- hibernate-ehcache
-
-
- net.sf.ehcache
- ehcache-core
-
-
- org.hibernate
- hibernate-c3p0
-
-
- org.commonjava.googlecode.markdown4j
- markdown4j
-
-
- com.jsuereth
- scala-arm_${scala.binary.version}
-
-
- com.twelvemonkeys.imageio
- imageio-jpeg
-
-
+
+
+ com.sothr.imagetools
+ hash
+
+
+ junit
+ junit
+ test
+
+
+ org.scalatest
+ scalatest_${scala.binary.version}
+ test
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ ch.qos.logback
+ logback-access
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.clapper
+ grizzled-slf4j_${scala.binary.version}
+
+
+ org.scala-lang
+ scala-library
+
+
+ com.jsuereth
+ scala-arm_${scala.binary.version}
+
+
+ net.coobird
+ thumbnailator
+
+
+ com.typesafe
+ config
+
+
+ net.sourceforge.jtransforms
+ jtransforms
+
+
+ commons-cli
+ commons-cli
+
+
+ commons-codec
+ commons-codec
+
+
+ javax.transaction
+ jta
+
+
+ net.sf.ehcache
+ ehcache
+
+
+ com.typesafe.akka
+ akka-actor_${scala.binary.version}
+
+
+ com.typesafe.akka
+ akka-slf4j_${scala.binary.version}
+
+
+ com.h2database
+ h2
+
+
+ org.hibernate
+ hibernate-core
+
+
+ org.hibernate
+ hibernate-ehcache
+
+
+ net.sf.ehcache
+ ehcache-core
+
+
+ org.hibernate
+ hibernate-c3p0
+
+
+ org.commonjava.googlecode.markdown4j
+ markdown4j
+
+
+ com.jsuereth
+ scala-arm_${scala.binary.version}
+
+
+ com.twelvemonkeys.imageio
+ imageio-jpeg
+
+
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 2.7
-
- false
-
-
-
-
- org.scalatest
- scalatest-maven-plugin
- 1.0-RC2
-
- ${project.build.directory}/surefire-reports
- .
- WDF TestSuite.txt
- -Xmx128m
-
-
-
- test
-
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 2.4
-
-
- package
-
- jar
-
-
-
-
- true
- lib/
-
-
-
- ${project.build.directory}/release
-
-
-
-
-
-
-
- maven-antrun-plugin
- 1.4
-
-
- prepare
- process-resources
-
-
-
-
-
-
-
-
-
-
-
-
- run
-
-
-
-
-
-
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.7
+
+ false
+
+
+
+
+ org.scalatest
+ scalatest-maven-plugin
+ 1.0-RC2
+
+ ${project.build.directory}/surefire-reports
+ .
+ WDF TestSuite.txt
+ -Xmx128m
+
+
+
+ test
+
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.4
+
+
+ package
+
+ jar
+
+
+
+
+ true
+ lib/
+
+
+
+ ${project.build.directory}/release
+
+
+
+
+
+
+
+ maven-antrun-plugin
+ 1.4
+
+
+ prepare
+ process-resources
+
+
+
+
+
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+
diff --git a/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java b/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java
index 3369ed1..b8e9bb1 100644
--- a/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java
+++ b/engine/src/main/java/com/sothr/imagetools/engine/AppConfig.java
@@ -16,130 +16,130 @@ import org.slf4j.LoggerFactory;
import java.io.File;
-public class AppConfig {
-
- // Logging defaults
- private static final String LOGSETTINGSFILE = "./logback.xml";
- // Properties defaults
- private static final String DEFAULTPROPERTIESFILE = "application.conf";
- private static final String USERPROPERTIESFILE = "user.conf";
- // General Akka Actor System
- private static final ActorSystem appSystem = ActorSystem.create("ITActorSystem");
- public static CacheManager cacheManager;
- public static FXMLLoader fxmlLoader = null;
- private static Logger logger;
- private static Boolean configuredLogging = false;
- private static Boolean loadedProperties = false;
- // Cache defaults
- private static Boolean configuredCache = false;
- // The Main App
- private static Stage primaryStage = null;
-
- public static Stage getPrimaryStage() {
- return primaryStage;
- }
-
- public static void setPrimaryStage(Stage newPrimaryStage) {
- primaryStage = newPrimaryStage;
- }
-
- public static FXMLLoader getFxmlLoader() {
- return fxmlLoader;
- }
-
- public static void setFxmlLoader(FXMLLoader loader) {
- fxmlLoader = loader;
- }
-
- public static ActorSystem getAppActorSystem() {
- return appSystem;
- }
-
- public static void configureApp() {
- logger = (Logger) LoggerFactory.getLogger(AppConfig.class);
- loadProperties();
- configLogging();
- configCache();
- }
-
- private static void configLogging(String location) {
- //Logging Config
- //remove previous configuration if it exists
- Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
- LoggerContext context = rootLogger.getLoggerContext();
- context.reset();
- File file = new File(location);
- Boolean fromFile = false;
- if (file.exists()) {
- fromFile = true;
- try {
- JoranConfigurator configurator = new JoranConfigurator();
- configurator.setContext(context);
- // Call context.reset() to clear any previous configuration, e.g. default
- // configuration. For multi-step configuration, omit calling context.reset().
- context.reset();
- configurator.doConfigure(location);
- } catch (JoranException je) {
- // StatusPrinter will handle this
- }
- StatusPrinter.printInCaseOfErrorsOrWarnings(context);
- } else {
- try {
- JoranConfigurator configurator = new JoranConfigurator();
- configurator.setContext(context);
- // Call context.reset() to clear any previous configuration, e.g. default
- // configuration. For multi-step configuration, omit calling context.reset().
- context.reset();
- configurator.doConfigure(ResourceLoader.get().getResourceStream("logback-minimum-config.xml"));
- } catch (JoranException je) {
- // StatusPrinter will handle this
- }
- StatusPrinter.printInCaseOfErrorsOrWarnings(context);
- }
- String message = fromFile ? "From File" : "From Defaults";
- logger.info(String.format("Configured Logger %s", message));
- logger.info(String.format("Detected Version: %s of Image Tools", PropertiesService.getVersion().toString()));
- logger.info(String.format("Running on %s, %s, %s", PropertiesService.OS(), PropertiesService.OS_VERSION(), PropertiesService.OS_ARCH()));
- }
-
- //Only configure logging from the default file once
- private static void configLogging() {
- if (!configuredLogging) {
- configLogging(LOGSETTINGSFILE);
- configuredLogging = true;
- logger.info("Configured logging");
- }
- }
-
- private static void loadProperties() {
- if (!loadedProperties) {
- File file = new File(USERPROPERTIESFILE);
- if (file.exists()) {
- PropertiesService.loadProperties(DEFAULTPROPERTIESFILE, USERPROPERTIESFILE);
- } else {
- PropertiesService.loadProperties(DEFAULTPROPERTIESFILE, null);
- }
- loadedProperties = true;
- logger.info("Loaded Properties");
- }
- }
-
- private static void configCache() {
- if (!configuredCache) {
- cacheManager = CacheManager.newInstance();
- configuredCache = true;
- logger.info("Configured EHCache");
- }
- }
-
- public static void shutdown() {
- saveProperties();
- HibernateUtil.getSessionFactory().close();
- }
-
- private static void saveProperties() {
- PropertiesService.saveConf(USERPROPERTIESFILE);
- logger.debug("Saved properties");
- }
-
-}
+public class AppConfig {
+
+ // Logging defaults
+ private static final String LOGSETTINGSFILE = "./logback.xml";
+ // Properties defaults
+ private static final String DEFAULTPROPERTIESFILE = "application.conf";
+ private static final String USERPROPERTIESFILE = "user.conf";
+ // General Akka Actor System
+ private static final ActorSystem appSystem = ActorSystem.create("ITActorSystem");
+ public static CacheManager cacheManager;
+ public static FXMLLoader fxmlLoader = null;
+ private static Logger logger;
+ private static Boolean configuredLogging = false;
+ private static Boolean loadedProperties = false;
+ // Cache defaults
+ private static Boolean configuredCache = false;
+ // The Main App
+ private static Stage primaryStage = null;
+
+ public static Stage getPrimaryStage() {
+ return primaryStage;
+ }
+
+ public static void setPrimaryStage(Stage newPrimaryStage) {
+ primaryStage = newPrimaryStage;
+ }
+
+ public static FXMLLoader getFxmlLoader() {
+ return fxmlLoader;
+ }
+
+ public static void setFxmlLoader(FXMLLoader loader) {
+ fxmlLoader = loader;
+ }
+
+ public static ActorSystem getAppActorSystem() {
+ return appSystem;
+ }
+
+ public static void configureApp() {
+ logger = (Logger) LoggerFactory.getLogger(AppConfig.class);
+ loadProperties();
+ configLogging();
+ configCache();
+ }
+
+ private static void configLogging(String location) {
+ //Logging Config
+ //remove previous configuration if it exists
+ Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+ LoggerContext context = rootLogger.getLoggerContext();
+ context.reset();
+ File file = new File(location);
+ Boolean fromFile = false;
+ if (file.exists()) {
+ fromFile = true;
+ try {
+ JoranConfigurator configurator = new JoranConfigurator();
+ configurator.setContext(context);
+ // Call context.reset() to clear any previous configuration, e.g. default
+ // configuration. For multi-step configuration, omit calling context.reset().
+ context.reset();
+ configurator.doConfigure(location);
+ } catch (JoranException je) {
+ // StatusPrinter will handle this
+ }
+ StatusPrinter.printInCaseOfErrorsOrWarnings(context);
+ } else {
+ try {
+ JoranConfigurator configurator = new JoranConfigurator();
+ configurator.setContext(context);
+ // Call context.reset() to clear any previous configuration, e.g. default
+ // configuration. For multi-step configuration, omit calling context.reset().
+ context.reset();
+ configurator.doConfigure(ResourceLoader.get().getResourceStream("logback-minimum-config.xml"));
+ } catch (JoranException je) {
+ // StatusPrinter will handle this
+ }
+ StatusPrinter.printInCaseOfErrorsOrWarnings(context);
+ }
+ String message = fromFile ? "From File" : "From Defaults";
+ logger.info(String.format("Configured Logger %s", message));
+ logger.info(String.format("Detected Version: %s of Image Tools", PropertiesService.getVersion().toString()));
+ logger.info(String.format("Running on %s, %s, %s", PropertiesService.OS(), PropertiesService.OS_VERSION(), PropertiesService.OS_ARCH()));
+ }
+
+ //Only configure logging from the default file once
+ private static void configLogging() {
+ if (!configuredLogging) {
+ configLogging(LOGSETTINGSFILE);
+ configuredLogging = true;
+ logger.info("Configured logging");
+ }
+ }
+
+ private static void loadProperties() {
+ if (!loadedProperties) {
+ File file = new File(USERPROPERTIESFILE);
+ if (file.exists()) {
+ PropertiesService.loadProperties(DEFAULTPROPERTIESFILE, USERPROPERTIESFILE);
+ } else {
+ PropertiesService.loadProperties(DEFAULTPROPERTIESFILE, null);
+ }
+ loadedProperties = true;
+ logger.info("Loaded Properties");
+ }
+ }
+
+ private static void configCache() {
+ if (!configuredCache) {
+ cacheManager = CacheManager.newInstance();
+ configuredCache = true;
+ logger.info("Configured EHCache");
+ }
+ }
+
+ public static void shutdown() {
+ saveProperties();
+ HibernateUtil.getSessionFactory().close();
+ }
+
+ private static void saveProperties() {
+ PropertiesService.saveConf(USERPROPERTIESFILE);
+ logger.debug("Saved properties");
+ }
+
+}
\ No newline at end of file
diff --git a/engine/src/main/java/com/sothr/imagetools/engine/errors/ImageToolsException.java b/engine/src/main/java/com/sothr/imagetools/engine/errors/ImageToolsException.java
index b4ae892..4d9eb2c 100644
--- a/engine/src/main/java/com/sothr/imagetools/engine/errors/ImageToolsException.java
+++ b/engine/src/main/java/com/sothr/imagetools/engine/errors/ImageToolsException.java
@@ -7,20 +7,20 @@ package com.sothr.imagetools.engine.errors;
*/
public class ImageToolsException extends Exception {
- public ImageToolsException() {
- super();
- }
+ public ImageToolsException() {
+ super();
+ }
- public ImageToolsException(String message) {
- super(message);
- }
+ public ImageToolsException(String message) {
+ super(message);
+ }
- public ImageToolsException(String message, Throwable cause) {
- super(message, cause);
- }
+ public ImageToolsException(String message, Throwable cause) {
+ super(message, cause);
+ }
- public ImageToolsException(Throwable cause) {
- super(cause);
- }
+ public ImageToolsException(Throwable cause) {
+ super(cause);
+ }
-}
+}
\ No newline at end of file
diff --git a/engine/src/main/java/com/sothr/imagetools/engine/image/ImageType.java b/engine/src/main/java/com/sothr/imagetools/engine/image/ImageType.java
index 610514e..cfb49ef 100644
--- a/engine/src/main/java/com/sothr/imagetools/engine/image/ImageType.java
+++ b/engine/src/main/java/com/sothr/imagetools/engine/image/ImageType.java
@@ -1,5 +1,5 @@
package com.sothr.imagetools.engine.image;
public enum ImageType {
- SingleFrameImage, MultiFrameImage
-}
+ SingleFrameImage, MultiFrameImage
+}
\ No newline at end of file
diff --git a/engine/src/main/java/com/sothr/imagetools/engine/util/ResourceLoader.java b/engine/src/main/java/com/sothr/imagetools/engine/util/ResourceLoader.java
index c85e6a5..8f793b7 100644
--- a/engine/src/main/java/com/sothr/imagetools/engine/util/ResourceLoader.java
+++ b/engine/src/main/java/com/sothr/imagetools/engine/util/ResourceLoader.java
@@ -13,26 +13,26 @@ import java.net.URL;
*/
public class ResourceLoader {
- private static final ResourceLoader instance = new ResourceLoader();
+ private static final ResourceLoader instance = new ResourceLoader();
- private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
- private ResourceLoader() {
- logger.info("Created Resource Loader");
- }
+ private ResourceLoader() {
+ logger.info("Created Resource Loader");
+ }
- public static ResourceLoader get() {
- return instance;
- }
+ public static ResourceLoader get() {
+ return instance;
+ }
- public URL getResource(String location) {
- logger.debug(String.format("Attempting to load resource: %s", location));
- return Thread.currentThread().getContextClassLoader().getResource(location);
- }
+ public URL getResource(String location) {
+ logger.debug(String.format("Attempting to load resource: %s", location));
+ return Thread.currentThread().getContextClassLoader().getResource(location);
+ }
- public InputStream getResourceStream(String location) {
- logger.debug(String.format("Attempting to get stream for resource: %s", location));
- return Thread.currentThread().getContextClassLoader().getResourceAsStream(location);
- }
+ public InputStream getResourceStream(String location) {
+ logger.debug(String.format("Attempting to get stream for resource: %s", location));
+ return Thread.currentThread().getContextClassLoader().getResourceAsStream(location);
+ }
-}
+}
\ No newline at end of file
diff --git a/engine/src/main/resources/ehcache.xml b/engine/src/main/resources/ehcache.xml
index ef3a96d..1a7019b 100644
--- a/engine/src/main/resources/ehcache.xml
+++ b/engine/src/main/resources/ehcache.xml
@@ -1,9 +1,9 @@
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/main/resources/hibernate.cfg.xml b/engine/src/main/resources/hibernate.cfg.xml
index 4de017a..63a59ae 100644
--- a/engine/src/main/resources/hibernate.cfg.xml
+++ b/engine/src/main/resources/hibernate.cfg.xml
@@ -1,51 +1,51 @@
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
-
-
+
+
-
- org.h2.Driver
-
- org.hibernate.dialect.H2Dialect
- false
-
-
-
- java:comp/UserTransaction
+
+ org.h2.Driver
+
+ org.hibernate.dialect.H2Dialect
+ false
+
+
+
+ java:comp/UserTransaction
- update
+ update
-
- thread
+
+ thread
-
- org.hibernate.cache.ehcache.EhCacheRegionFactory
-
- org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
-
- true
+
+ org.hibernate.cache.ehcache.EhCacheRegionFactory
+
+ org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
+
+ true
- 1
- 100
-
- 50
- 0
- 5
- 100
-
+ 1
+ 100
+
+ 50
+ 0
+ 5
+ 100
+
-
-
-
+
+
+
-
-
-
+
+
+
\ No newline at end of file
diff --git a/engine/src/main/resources/hibernate/Image.hbm.xml b/engine/src/main/resources/hibernate/Image.hbm.xml
index bbe679b..c662c7a 100644
--- a/engine/src/main/resources/hibernate/Image.hbm.xml
+++ b/engine/src/main/resources/hibernate/Image.hbm.xml
@@ -1,17 +1,17 @@
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
-
-
- This class contains the image hashes and meta data
-
-
-
-
-
-
-
+
+
+ This class contains the image hashes and meta data
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/main/resources/hibernate/ImageHash.hbm.xml b/engine/src/main/resources/hibernate/ImageHash.hbm.xml
index 3d79acb..7cac328 100644
--- a/engine/src/main/resources/hibernate/ImageHash.hbm.xml
+++ b/engine/src/main/resources/hibernate/ImageHash.hbm.xml
@@ -1,18 +1,18 @@
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
-
-
- This class contains the image hashes
-
-
-
-
-
-
-
-
-
+
+
+ This class contains the image hashes
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/main/resources/logback-minimum-config.xml b/engine/src/main/resources/logback-minimum-config.xml
index 3d5fd20..0203de6 100644
--- a/engine/src/main/resources/logback-minimum-config.xml
+++ b/engine/src/main/resources/logback-minimum-config.xml
@@ -1,23 +1,23 @@
-
- ImageTools.err
-
- false
- [%date{HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- ERROR
-
-
- 5
- ImageTools.err.%i
-
-
- 500KB
-
-
-
-
-
+
+ ImageTools.err
+
+ false
+ [%date{HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ ERROR
+
+
+ 5
+ ImageTools.err.%i
+
+
+ 500KB
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala b/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
index 7ad6e0e..d245b0c 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/ConcurrentEngine.scala
@@ -7,101 +7,101 @@ import akka.actor.{Actor, ActorLogging, ActorRef, PoisonPill, Props}
import akka.pattern.ask
import akka.routing.{Broadcast, RoundRobinPool, SmallestMailboxPool}
import akka.util.Timeout
-import com.sothr.imagetools.hash.{HashService, ImageHash}
import com.sothr.imagetools.engine.image.{Image, ImageService, SimilarImages}
import com.sothr.imagetools.engine.util.{PropertiesService, PropertyEnum}
+import com.sothr.imagetools.hash.HashService
import scala.collection.mutable
import scala.concurrent.Await
class ConcurrentEngine extends Engine with grizzled.slf4j.Logging {
- val engineProcessingController = system.actorOf(Props[ConcurrentEngineProcessingController], name = "EngineProcessingController")
- val engineSimilarityController = system.actorOf(Props[ConcurrentEngineSimilarityController], name = "EngineSimilarityController")
- implicit val timeout = Timeout(30, TimeUnit.SECONDS)
-
- override def setSearchedListener(listenerRef: ActorRef) = {
- this.searchedListener = listenerRef
- }
-
- override def setProcessedListener(listenerRef: ActorRef) = {
- engineProcessingController ! SetNewListener(listenerRef)
- }
-
- override def setSimilarityListener(listenerRef: ActorRef) = {
- engineSimilarityController ! SetNewListener(listenerRef)
- }
-
- //needs to be rebuilt
- def getSimilarImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[SimilarImages] = {
- debug(s"Looking for similar images in directory: $directoryPath")
- val images = getImagesForDirectory(directoryPath, recursive, recursiveDepth)
- info(s"Searching ${images.length} images for similarities")
- // make sure the engine is listening
- engineSimilarityController ! EngineStart
- for (rootImage <- images) {
- debug(s"Looking for images similar to: ${rootImage.imagePath}")
- engineSimilarityController ! EngineCompareImages(rootImage, images)
- }
- //tell the comparison engine there's nothing left to compare
- engineSimilarityController ! EngineNoMoreComparisons
- var doneProcessing = false
- while (!doneProcessing) {
- val f = engineSimilarityController ? EngineIsSimilarityFinished
- val result = Await.result(f, timeout.duration).asInstanceOf[Boolean]
- result match {
- case true =>
- doneProcessing = true
- debug("Processing Complete")
- case false =>
- debug("Still Processing")
- //sleep thread
- Thread.sleep(5000L)
- //val future = Future { blocking(Thread.sleep(5000L)); "done" }
- }
- }
- val f = engineSimilarityController ? EngineGetSimilarityResults
- val result = Await.result(f, timeout.duration).asInstanceOf[List[SimilarImages]]
-
- val cleanedSimilarImages = this.processSimilarities(result)
- var similarCount = 0
- for (similarImage <- cleanedSimilarImages) {
- similarCount += 1 + similarImage.similarImages.size
- }
-
- info(s"Finished processing ${images.size} images. Found $similarCount similar images")
- cleanedSimilarImages
- }
-
- def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = {
- debug(s"Looking for images in directory: $directoryPath")
- val imageFiles = getAllImageFiles(directoryPath, recursive, recursiveDepth)
- val images: mutable.MutableList[Image] = new mutable.MutableList[Image]()
- // make sure the engine is listening
- engineProcessingController ! EngineStart
- for (file <- imageFiles) {
- engineProcessingController ! EngineProcessFile(file)
- }
- engineProcessingController ! EngineNoMoreFiles
- var doneProcessing = false
- while (!doneProcessing) {
- val f = engineProcessingController ? EngineIsProcessingFinished
- val result = Await.result(f, timeout.duration).asInstanceOf[Boolean]
- result match {
- case true =>
- doneProcessing = true
- debug("Processing Complete")
- case false =>
- debug("Still Processing")
- //sleep thread
- Thread.sleep(5000L)
- //val future = Future { blocking(Thread.sleep(5000L)); "done" }
- }
- }
- val f = engineProcessingController ? EngineGetProcessingResults
- val result = Await.result(f, timeout.duration).asInstanceOf[List[Image]]
- images ++= result
- images.toList
- }
+ val engineProcessingController = system.actorOf(Props[ConcurrentEngineProcessingController], name = "EngineProcessingController")
+ val engineSimilarityController = system.actorOf(Props[ConcurrentEngineSimilarityController], name = "EngineSimilarityController")
+ implicit val timeout = Timeout(30, TimeUnit.SECONDS)
+
+ override def setSearchedListener(listenerRef: ActorRef) = {
+ this.searchedListener = listenerRef
+ }
+
+ override def setProcessedListener(listenerRef: ActorRef) = {
+ engineProcessingController ! SetNewListener(listenerRef)
+ }
+
+ override def setSimilarityListener(listenerRef: ActorRef) = {
+ engineSimilarityController ! SetNewListener(listenerRef)
+ }
+
+ //needs to be rebuilt
+ def getSimilarImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[SimilarImages] = {
+ debug(s"Looking for similar images in directory: $directoryPath")
+ val images = getImagesForDirectory(directoryPath, recursive, recursiveDepth)
+ info(s"Searching ${images.length} images for similarities")
+ // make sure the engine is listening
+ engineSimilarityController ! EngineStart
+ for (rootImage <- images) {
+ debug(s"Looking for images similar to: ${rootImage.imagePath}")
+ engineSimilarityController ! EngineCompareImages(rootImage, images)
+ }
+ //tell the comparison engine there's nothing left to compare
+ engineSimilarityController ! EngineNoMoreComparisons
+ var doneProcessing = false
+ while (!doneProcessing) {
+ val f = engineSimilarityController ? EngineIsSimilarityFinished
+ val result = Await.result(f, timeout.duration).asInstanceOf[Boolean]
+ result match {
+ case true =>
+ doneProcessing = true
+ debug("Processing Complete")
+ case false =>
+ debug("Still Processing")
+ //sleep thread
+ Thread.sleep(5000L)
+ //val future = Future { blocking(Thread.sleep(5000L)); "done" }
+ }
+ }
+ val f = engineSimilarityController ? EngineGetSimilarityResults
+ val result = Await.result(f, timeout.duration).asInstanceOf[List[SimilarImages]]
+
+ val cleanedSimilarImages = this.processSimilarities(result)
+ var similarCount = 0
+ for (similarImage <- cleanedSimilarImages) {
+ similarCount += 1 + similarImage.similarImages.size
+ }
+
+ info(s"Finished processing ${images.size} images. Found $similarCount similar images")
+ cleanedSimilarImages
+ }
+
+ def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = {
+ debug(s"Looking for images in directory: $directoryPath")
+ val imageFiles = getAllImageFiles(directoryPath, recursive, recursiveDepth)
+ val images: mutable.MutableList[Image] = new mutable.MutableList[Image]()
+ // make sure the engine is listening
+ engineProcessingController ! EngineStart
+ for (file <- imageFiles) {
+ engineProcessingController ! EngineProcessFile(file)
+ }
+ engineProcessingController ! EngineNoMoreFiles
+ var doneProcessing = false
+ while (!doneProcessing) {
+ val f = engineProcessingController ? EngineIsProcessingFinished
+ val result = Await.result(f, timeout.duration).asInstanceOf[Boolean]
+ result match {
+ case true =>
+ doneProcessing = true
+ debug("Processing Complete")
+ case false =>
+ debug("Still Processing")
+ //sleep thread
+ Thread.sleep(5000L)
+ //val future = Future { blocking(Thread.sleep(5000L)); "done" }
+ }
+ }
+ val f = engineProcessingController ? EngineGetProcessingResults
+ val result = Await.result(f, timeout.duration).asInstanceOf[List[Image]]
+ images ++= result
+ images.toList
+ }
}
@@ -127,144 +127,144 @@ case object EngineActorProcessingFinished
case object EngineActorReactivate
class ConcurrentEngineProcessingController extends Actor with ActorLogging {
- val numOfRouters = {
- val max = PropertiesService.get(PropertyEnum.ConcurrentProcessingLimit.toString).toInt
- val processors = Runtime.getRuntime.availableProcessors()
- var threads = 0
- if (processors > max) threads = max else if (processors > 1) threads = processors - 1 else threads = 1
- threads
- }
- var router = context.actorOf(Props[ConcurrentEngineProcessingActor].withRouter(SmallestMailboxPool(nrOfInstances = numOfRouters)))
-
- var images: mutable.MutableList[Image] = new mutable.MutableList[Image]()
- var toProcess = 0
- var processed = 0
-
- var processorsFinished = 0
- var listener = context.actorOf(Props[DefaultLoggingEngineListener],
- name = "ProcessedEngineListener")
-
- override def preStart() = {
- log.info("Staring the controller for processing images")
- log.info("Using {} actors to process images", numOfRouters)
- }
-
- override def receive = {
- case command: SetNewListener => setListener(command.listenerType)
- case command: EngineProcessFile => processFile(command)
- case command: EngineFileProcessed => fileProcessed(command)
- case EngineStart => startEngine()
- case EngineNoMoreFiles => requestWrapup()
- case EngineActorProcessingFinished => actorProcessingFinished()
- case EngineIsProcessingFinished => checkIfProcessingIsFinished()
- case EngineGetProcessingResults => checkForResults()
- case _ => log.info("received unknown message")
- }
-
- def setListener(newListener: ActorRef) = {
- //remove the old listener
- this.listener ! PoisonPill
- //setup the new listener
- this.listener = newListener
- }
-
- def startEngine() = {
- router ! Broadcast(EngineActorReactivate)
- }
-
- def processFile(command: EngineProcessFile) = {
- log.debug(s"Started evaluating ${command.file.getAbsolutePath}")
- toProcess += 1
- router ! command
- }
-
- def fileProcessed(command: EngineFileProcessed) = {
- processed += 1
- if (processed % 25 == 0 || processed == toProcess) {
- //log.info(s"Processed $processed/$toProcess")
- listener ! ComparedFileCount(processed, toProcess)
- }
- if (command.image != null) {
- log.debug(s"processed image: ${command.image.imagePath}")
- images += command.image
- }
- }
-
- def requestWrapup() = {
- router ! Broadcast(EngineNoMoreFiles)
- }
-
- /*
- * Record that a processor is done
- */
- def actorProcessingFinished() = {
- processorsFinished += 1
- }
-
- /*
- * Check if processing is done
- */
- def checkIfProcessingIsFinished() = {
- try {
- if (processorsFinished >= numOfRouters) sender ! true else sender ! false
- } catch {
- case e: Exception ⇒
- sender ! akka.actor.Status.Failure(e)
- throw e
- }
- }
-
- /*
- * Get the results of the processing
- */
- def checkForResults() = {
- try {
- listener ! SubmitMessage(s"Finished processing $processed/$processed images")
- processorsFinished = 0
- toProcess = 0
- processed = 0
- sender ! images.toList
- images.clear()
- } catch {
- case e: Exception ⇒
- sender ! akka.actor.Status.Failure(e)
- throw e
- }
- }
-
- override def postStop() = {
- super.postStop()
- this.listener ! PoisonPill
- }
+ val numOfRouters = {
+ val max = PropertiesService.get(PropertyEnum.ConcurrentProcessingLimit.toString).toInt
+ val processors = Runtime.getRuntime.availableProcessors()
+ var threads = 0
+ if (processors > max) threads = max else if (processors > 1) threads = processors - 1 else threads = 1
+ threads
+ }
+ var router = context.actorOf(Props[ConcurrentEngineProcessingActor].withRouter(SmallestMailboxPool(nrOfInstances = numOfRouters)))
+
+ var images: mutable.MutableList[Image] = new mutable.MutableList[Image]()
+ var toProcess = 0
+ var processed = 0
+
+ var processorsFinished = 0
+ var listener = context.actorOf(Props[DefaultLoggingEngineListener],
+ name = "ProcessedEngineListener")
+
+ override def preStart() = {
+ log.info("Staring the controller for processing images")
+ log.info("Using {} actors to process images", numOfRouters)
+ }
+
+ override def receive = {
+ case command: SetNewListener => setListener(command.listenerType)
+ case command: EngineProcessFile => processFile(command)
+ case command: EngineFileProcessed => fileProcessed(command)
+ case EngineStart => startEngine()
+ case EngineNoMoreFiles => requestWrapup()
+ case EngineActorProcessingFinished => actorProcessingFinished()
+ case EngineIsProcessingFinished => checkIfProcessingIsFinished()
+ case EngineGetProcessingResults => checkForResults()
+ case _ => log.info("received unknown message")
+ }
+
+ def setListener(newListener: ActorRef) = {
+ //remove the old listener
+ this.listener ! PoisonPill
+ //setup the new listener
+ this.listener = newListener
+ }
+
+ def startEngine() = {
+ router ! Broadcast(EngineActorReactivate)
+ }
+
+ def processFile(command: EngineProcessFile) = {
+ log.debug(s"Started evaluating ${command.file.getAbsolutePath}")
+ toProcess += 1
+ router ! command
+ }
+
+ def fileProcessed(command: EngineFileProcessed) = {
+ processed += 1
+ if (processed % 25 == 0 || processed == toProcess) {
+ //log.info(s"Processed $processed/$toProcess")
+ listener ! ComparedFileCount(processed, toProcess)
+ }
+ if (command.image != null) {
+ log.debug(s"processed image: ${command.image.imagePath}")
+ images += command.image
+ }
+ }
+
+ def requestWrapup() = {
+ router ! Broadcast(EngineNoMoreFiles)
+ }
+
+ /*
+ * Record that a processor is done
+ */
+ def actorProcessingFinished() = {
+ processorsFinished += 1
+ }
+
+ /*
+ * Check if processing is done
+ */
+ def checkIfProcessingIsFinished() = {
+ try {
+ if (processorsFinished >= numOfRouters) sender ! true else sender ! false
+ } catch {
+ case e: Exception ⇒
+ sender ! akka.actor.Status.Failure(e)
+ throw e
+ }
+ }
+
+ /*
+ * Get the results of the processing
+ */
+ def checkForResults() = {
+ try {
+ listener ! SubmitMessage(s"Finished processing $processed/$processed images")
+ processorsFinished = 0
+ toProcess = 0
+ processed = 0
+ sender ! images.toList
+ images.clear()
+ } catch {
+ case e: Exception ⇒
+ sender ! akka.actor.Status.Failure(e)
+ throw e
+ }
+ }
+
+ override def postStop() = {
+ super.postStop()
+ this.listener ! PoisonPill
+ }
}
class ConcurrentEngineProcessingActor extends Actor with ActorLogging {
- var ignoreMessages = false
-
- override def receive = {
- case command: EngineProcessFile => processFile(command)
- case EngineNoMoreFiles => finishedProcessingFiles()
- case EngineActorReactivate => ignoreMessages = false
- case _ => log.info("received unknown message")
- }
-
- def processFile(command: EngineProcessFile) = {
- if (!ignoreMessages) {
- val image = ImageService.getImage(command.file)
- if (image != null) {
- sender ! EngineFileProcessed(image)
- } else {
- log.debug(s"Failed to process image: ${command.file.getAbsolutePath}")
- }
- }
- }
-
- def finishedProcessingFiles() = {
- if (!ignoreMessages) {
- ignoreMessages = true
- sender ! EngineActorProcessingFinished
- }
- }
+ var ignoreMessages = false
+
+ override def receive = {
+ case command: EngineProcessFile => processFile(command)
+ case EngineNoMoreFiles => finishedProcessingFiles()
+ case EngineActorReactivate => ignoreMessages = false
+ case _ => log.info("received unknown message")
+ }
+
+ def processFile(command: EngineProcessFile) = {
+ if (!ignoreMessages) {
+ val image = ImageService.getImage(command.file)
+ if (image != null) {
+ sender ! EngineFileProcessed(image)
+ } else {
+ log.debug(s"Failed to process image: ${command.file.getAbsolutePath}")
+ }
+ }
+ }
+
+ def finishedProcessingFiles() = {
+ if (!ignoreMessages) {
+ ignoreMessages = true
+ sender ! EngineActorProcessingFinished
+ }
+ }
}
//finding similarities between images
@@ -281,165 +281,165 @@ case object EngineGetSimilarityResults
case object EngineActorCompareImagesFinished
class ConcurrentEngineSimilarityController extends Actor with ActorLogging {
- val numOfRouters = {
- val max = PropertiesService.get(PropertyEnum.ConcurrentSimilarityLimit.toString).toInt
- val processors = Runtime.getRuntime.availableProcessors()
- var threads = 0
- if (processors > max) threads = max else if (processors > 1) threads = processors - 1 else threads = 1
- threads
- }
- val router = context.actorOf(Props[ConcurrentEngineSimilarityActor].withRouter(RoundRobinPool(nrOfInstances = numOfRouters)))
-
- val allSimilarImages = new mutable.MutableList[SimilarImages]
- var toProcess = 0
- var processed = 0
-
- var processorsFinished = 0
-
- var listener = context.actorOf(Props[DefaultLoggingEngineListener],
- name = "SimilarityEngineListener")
-
- override def preStart() = {
- log.info("Staring the controller for processing similarities between images")
- log.info("Using {} actors to process image similarities", numOfRouters)
- }
-
- override def receive = {
- case command: SetNewListener => setListener(command.listenerType)
- case command: EngineCompareImages => findSimilarities(command)
- case command: EngineCompareImagesComplete => similarityProcessed(command)
- case EngineStart => startEngine()
- case EngineNoMoreComparisons => requestWrapup()
- case EngineActorCompareImagesFinished => actorProcessingFinished()
- case EngineIsSimilarityFinished => checkIfProcessingIsFinished()
- case EngineGetSimilarityResults => checkForResults()
- case _ => log.info("received unknown message")
- }
-
- def setListener(newListener: ActorRef) = {
- //remove the old listener
- this.listener ! PoisonPill
- //setup the new listener
- this.listener = newListener
- }
-
- def startEngine() = {
- router ! Broadcast(EngineActorReactivate)
- }
-
- def findSimilarities(command: EngineCompareImages) = {
- log.debug(s"Finding similarities between {} and {} images", command.image1.imagePath, command.images.length)
- toProcess += 1
- if (toProcess % 250 == 0) {
- //log.info("Sent {} images to be processed for similarites", toProcess)
- listener ! SubmitMessage(s"Sent $toProcess images to be processed for similarites")
- }
- //just relay the command to our workers
- router ! command
- }
-
- def similarityProcessed(command: EngineCompareImagesComplete) = {
- processed += 1
- if (processed % 25 == 0 || processed == toProcess) {
- //log.info(s"Processed $processed/$toProcess")
- listener ! ScannedFileCount(processed, toProcess)
- }
- if (command.similarImages != null) {
- log.debug(s"Found similar images: ${command.similarImages}")
- allSimilarImages += command.similarImages
- }
- }
-
- def requestWrapup() = {
- router ! Broadcast(EngineNoMoreComparisons)
- }
-
- /*
- * Record that a processor is done
- */
- def actorProcessingFinished() = {
- processorsFinished += 1
- log.debug("Similarity Processor Reported Finished")
- }
-
- /*
- * Check if processing is done
- */
- def checkIfProcessingIsFinished() = {
- try {
- log.debug("Processors finished {}/{}", processorsFinished, numOfRouters)
- if (processorsFinished >= numOfRouters) sender ! true else sender ! false
- } catch {
- case e: Exception ⇒
- sender ! akka.actor.Status.Failure(e)
- throw e
- }
- }
-
- /*
- * Get the results of the processing
- */
- def checkForResults() = {
- try {
- listener ! SubmitMessage(s"Finished scanning $processed/$processed images")
- processorsFinished = 0
- toProcess = 0
- processed = 0
- sender ! allSimilarImages.toList
- allSimilarImages.clear()
- } catch {
- case e: Exception ⇒
- sender ! akka.actor.Status.Failure(e)
- throw e
- }
- }
-
- override def postStop() = {
- super.postStop()
- this.listener ! PoisonPill
- }
+ val numOfRouters = {
+ val max = PropertiesService.get(PropertyEnum.ConcurrentSimilarityLimit.toString).toInt
+ val processors = Runtime.getRuntime.availableProcessors()
+ var threads = 0
+ if (processors > max) threads = max else if (processors > 1) threads = processors - 1 else threads = 1
+ threads
+ }
+ val router = context.actorOf(Props[ConcurrentEngineSimilarityActor].withRouter(RoundRobinPool(nrOfInstances = numOfRouters)))
+
+ val allSimilarImages = new mutable.MutableList[SimilarImages]
+ var toProcess = 0
+ var processed = 0
+
+ var processorsFinished = 0
+
+ var listener = context.actorOf(Props[DefaultLoggingEngineListener],
+ name = "SimilarityEngineListener")
+
+ override def preStart() = {
+ log.info("Staring the controller for processing similarities between images")
+ log.info("Using {} actors to process image similarities", numOfRouters)
+ }
+
+ override def receive = {
+ case command: SetNewListener => setListener(command.listenerType)
+ case command: EngineCompareImages => findSimilarities(command)
+ case command: EngineCompareImagesComplete => similarityProcessed(command)
+ case EngineStart => startEngine()
+ case EngineNoMoreComparisons => requestWrapup()
+ case EngineActorCompareImagesFinished => actorProcessingFinished()
+ case EngineIsSimilarityFinished => checkIfProcessingIsFinished()
+ case EngineGetSimilarityResults => checkForResults()
+ case _ => log.info("received unknown message")
+ }
+
+ def setListener(newListener: ActorRef) = {
+ //remove the old listener
+ this.listener ! PoisonPill
+ //setup the new listener
+ this.listener = newListener
+ }
+
+ def startEngine() = {
+ router ! Broadcast(EngineActorReactivate)
+ }
+
+ def findSimilarities(command: EngineCompareImages) = {
+ log.debug(s"Finding similarities between {} and {} images", command.image1.imagePath, command.images.length)
+ toProcess += 1
+ if (toProcess % 250 == 0) {
+ //log.info("Sent {} images to be processed for similarites", toProcess)
+ listener ! SubmitMessage(s"Sent $toProcess images to be processed for similarites")
+ }
+ //just relay the command to our workers
+ router ! command
+ }
+
+ def similarityProcessed(command: EngineCompareImagesComplete) = {
+ processed += 1
+ if (processed % 25 == 0 || processed == toProcess) {
+ //log.info(s"Processed $processed/$toProcess")
+ listener ! ScannedFileCount(processed, toProcess)
+ }
+ if (command.similarImages != null) {
+ log.debug(s"Found similar images: ${command.similarImages}")
+ allSimilarImages += command.similarImages
+ }
+ }
+
+ def requestWrapup() = {
+ router ! Broadcast(EngineNoMoreComparisons)
+ }
+
+ /*
+ * Record that a processor is done
+ */
+ def actorProcessingFinished() = {
+ processorsFinished += 1
+ log.debug("Similarity Processor Reported Finished")
+ }
+
+ /*
+ * Check if processing is done
+ */
+ def checkIfProcessingIsFinished() = {
+ try {
+ log.debug("Processors finished {}/{}", processorsFinished, numOfRouters)
+ if (processorsFinished >= numOfRouters) sender ! true else sender ! false
+ } catch {
+ case e: Exception ⇒
+ sender ! akka.actor.Status.Failure(e)
+ throw e
+ }
+ }
+
+ /*
+ * Get the results of the processing
+ */
+ def checkForResults() = {
+ try {
+ listener ! SubmitMessage(s"Finished scanning $processed/$processed images")
+ processorsFinished = 0
+ toProcess = 0
+ processed = 0
+ sender ! allSimilarImages.toList
+ allSimilarImages.clear()
+ } catch {
+ case e: Exception ⇒
+ sender ! akka.actor.Status.Failure(e)
+ throw e
+ }
+ }
+
+ override def postStop() = {
+ super.postStop()
+ this.listener ! PoisonPill
+ }
}
class ConcurrentEngineSimilarityActor extends Actor with ActorLogging {
- var ignoreMessages = false
-
- override def receive = {
- case command: EngineCompareImages => compareImages(command)
- case EngineNoMoreComparisons => finishedComparisons()
- case EngineActorReactivate => ignoreMessages = false
- case _ => log.info("received unknown message")
- }
-
- def compareImages(command: EngineCompareImages) = {
- if (!ignoreMessages) {
- val similarImages = new mutable.MutableList[Image]()
- for (image <- command.images) {
- if (!command.image1.equals(image)) {
- val image1Hashes = ImageService.convertToImageHashDTO(command.image1.hashes)
- val imageHashes = ImageService.convertToImageHashDTO(image.hashes)
- if (HashService.areImageHashesSimilar(PropertiesService.aHashSettings, PropertiesService.dHashSettings, PropertiesService.pHashSettings, image1Hashes, imageHashes)) {
- similarImages += image
- }
- }
- }
- //only send a message if we find similar images
- if (similarImages.length >= 1) {
- similarImages += command.image1
- val similarImage = new SimilarImages(similarImages.toSet)
- log.debug(s"Found ${similarImage.similarImages.size} similar images to ${command.image1}")
- sender ! EngineCompareImagesComplete(similarImage)
- } else {
- log.debug(s"Found no similar images to ${command.image1}")
- sender ! EngineCompareImagesComplete(null)
- }
- }
- }
-
- def finishedComparisons() = {
- if (!ignoreMessages) {
- ignoreMessages = true
- log.debug("Finished processing comparisons")
- sender ! EngineActorCompareImagesFinished
- }
- }
+ var ignoreMessages = false
+
+ override def receive = {
+ case command: EngineCompareImages => compareImages(command)
+ case EngineNoMoreComparisons => finishedComparisons()
+ case EngineActorReactivate => ignoreMessages = false
+ case _ => log.info("received unknown message")
+ }
+
+ def compareImages(command: EngineCompareImages) = {
+ if (!ignoreMessages) {
+ val similarImages = new mutable.MutableList[Image]()
+ for (image <- command.images) {
+ if (!command.image1.equals(image)) {
+ val image1Hashes = ImageService.convertToImageHashDTO(command.image1.hashes)
+ val imageHashes = ImageService.convertToImageHashDTO(image.hashes)
+ if (HashService.areImageHashesSimilar(PropertiesService.aHashSettings, PropertiesService.dHashSettings, PropertiesService.pHashSettings, image1Hashes, imageHashes)) {
+ similarImages += image
+ }
+ }
+ }
+ //only send a message if we find similar images
+ if (similarImages.length >= 1) {
+ similarImages += command.image1
+ val similarImage = new SimilarImages(similarImages.toSet)
+ log.debug(s"Found ${similarImage.similarImages.size} similar images to ${command.image1}")
+ sender ! EngineCompareImagesComplete(similarImage)
+ } else {
+ log.debug(s"Found no similar images to ${command.image1}")
+ sender ! EngineCompareImagesComplete(null)
+ }
+ }
+ }
+
+ def finishedComparisons() = {
+ if (!ignoreMessages) {
+ ignoreMessages = true
+ log.debug("Finished processing comparisons")
+ sender ! EngineActorCompareImagesFinished
+ }
+ }
}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/Engine.scala b/engine/src/main/scala/com/sothr/imagetools/engine/Engine.scala
index c4df2e0..b6bb36a 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/Engine.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/Engine.scala
@@ -12,133 +12,135 @@ import scala.collection.mutable
import scala.util.control.Breaks._
/**
- * Engine definition
- *
- * Created by drew on 1/26/14.
- */
+ * Engine definition
+ *
+ * Created by drew on 1/26/14.
+ */
abstract class Engine extends Logging {
- val system = ActorSystem("EngineActorSystem")
- val imageFilter: ImageFilter = new ImageFilter()
- val imageCache = AppConfig.cacheManager.getCache("images")
-
- //file search listener
- var searchedListener = system.actorOf(Props[DefaultLoggingEngineListener],
- name = "SearchedEngineListener")
-
- def setSearchedListener(listenerRef: ActorRef) = {
- this.searchedListener = listenerRef
- }
-
- def setProcessedListener(listenerType: ActorRef)
-
- def setSimilarityListener(listenerType: ActorRef)
-
- def getAllImageFiles(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[File] = {
- val fileList = new mutable.MutableList[File]()
- if (directoryPath != null && directoryPath != "") {
- val directory: File = new File(directoryPath)
- val imageFilter = new ImageFilter
- if (directory.isDirectory) {
- val files = directory.listFiles(imageFilter)
- if (files != null) {
- fileList ++= files
- debug(s"Found ${files.length} files that are images in directory: $directoryPath")
- if (recursive) {
- val directoryFilter = new DirectoryFilter
- val directories = directory.listFiles(directoryFilter)
- for (directory <- directories) {
- fileList ++= getAllImageFiles(directory.getAbsolutePath, recursive, recursiveDepth - 1)
- this.searchedListener ! SubmitMessage(s"Found ${fileList.length} files to process")
- }
- } else {
- this.searchedListener ! SubmitMessage(s"Found ${fileList.length} files to process")
- }
- }
- }
- }
- fileList.toList
- }
-
- /**
- * Get all images for a directory with hashes
- */
- def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image]
-
- /**
- * Get all similar images for a directory with hashes
- */
- def getSimilarImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[SimilarImages]
-
- def deleteImage(image: Image): Unit = {
- ImageService.deleteImage(image)
- }
-
- def deleteImages(images: List[Image]): Unit = {
- for (image <- images) {
- deleteImage(image)
- }
- }
-
- /**
- * Go through the list of similarities and group them so that they represent actual similarities
- *
- * For example. A is similar to B and C is similar to B but A is was not considered similar to C. Therefore A B and C should be considered similar unless otherwise noted.
- *
- * @param similarList a list of detected similar images
- * @return a grouped and combined list of similar images
- */
- def processSimilarities(similarList: List[SimilarImages]): List[SimilarImages] = {
- //process the result into a list we want in cleanedSimilarImages
- /*
- Go through all the images. If a similar image for the image doesn't exist, create it. if it does,
- add it to that similar image. The end result is that all images should be grouped according to
- their similar images and images that are similar to them.
- */
- var count = 0
- // similar image mapping to map known images back to their similar set
- val similarImageMap = new mutable.HashMap[Image, SimilarImages]()
-
- // List of the actual similar image sets
- val cleanedSimilarImages = new mutable.MutableList[SimilarImages]()
-
- // loop over all of the similar image sets
- for (similarImages <- similarList) {
- count += 1
- if (count % 25 == 0 || count == similarList.length) {
- debug(s"Cleaning similar images. $count/${similarList.length} ${similarList.length - count} left to clean")
- this.searchedListener ! SubmitMessage(s"Cleaning similar images. $count/${similarList.length}")
- }
- var foundSimilarity = false
- var similarity:SimilarImages = null
- breakable { for (similarImage <- similarImages.similarImages) {
- if (similarImageMap.contains(similarImage)) {
- similarity = similarImageMap(similarImage)
- foundSimilarity = true
- break()
- }
- } }
-
- //if no similarity was found, one should be created
- if (!foundSimilarity) {
- similarity = new SimilarImages(new HashSet[Image])
- // the created similarity is added to the cleaned list
- cleanedSimilarImages += similarity
- }
-
- // all images should be added to this new similarity
- similarity.similarImages = similarity.similarImages ++ similarImages.similarImages
- similarImages.similarImages.foreach(img => similarImageMap.put(img, similarity))
- }
-
- //get a count of similar images found
- var totalCount = 0
- cleanedSimilarImages.foreach(img => totalCount += img.similarImages.size)
- debug(s"Found $totalCount images with similarities")
- this.searchedListener ! SubmitMessage(s"Found $totalCount images with similarities")
-
- // Sort the similarImages by ?!?!?!? and return
- cleanedSimilarImages.toList.sorted(SimilarImagesOrdering)
- }
+ val system = ActorSystem("EngineActorSystem")
+ val imageFilter: ImageFilter = new ImageFilter()
+ val imageCache = AppConfig.cacheManager.getCache("images")
+
+ //file search listener
+ var searchedListener = system.actorOf(Props[DefaultLoggingEngineListener],
+ name = "SearchedEngineListener")
+
+ def setSearchedListener(listenerRef: ActorRef) = {
+ this.searchedListener = listenerRef
+ }
+
+ def setProcessedListener(listenerType: ActorRef)
+
+ def setSimilarityListener(listenerType: ActorRef)
+
+ def getAllImageFiles(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[File] = {
+ val fileList = new mutable.MutableList[File]()
+ if (directoryPath != null && directoryPath != "") {
+ val directory: File = new File(directoryPath)
+ val imageFilter = new ImageFilter
+ if (directory.isDirectory) {
+ val files = directory.listFiles(imageFilter)
+ if (files != null) {
+ fileList ++= files
+ debug(s"Found ${files.length} files that are images in directory: $directoryPath")
+ if (recursive) {
+ val directoryFilter = new DirectoryFilter
+ val directories = directory.listFiles(directoryFilter)
+ for (directory <- directories) {
+ fileList ++= getAllImageFiles(directory.getAbsolutePath, recursive, recursiveDepth - 1)
+ this.searchedListener ! SubmitMessage(s"Found ${fileList.length} files to process")
+ }
+ } else {
+ this.searchedListener ! SubmitMessage(s"Found ${fileList.length} files to process")
+ }
+ }
+ }
+ }
+ fileList.toList
+ }
+
+ /**
+ * Get all images for a directory with hashes
+ */
+ def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image]
+
+ /**
+ * Get all similar images for a directory with hashes
+ */
+ def getSimilarImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[SimilarImages]
+
+ def deleteImages(images: List[Image]): Unit = {
+ for (image <- images) {
+ deleteImage(image)
+ }
+ }
+
+ def deleteImage(image: Image): Unit = {
+ ImageService.deleteImage(image)
+ }
+
+ /**
+ * Go through the list of similarities and group them so that they represent actual similarities
+ *
+ * For example. A is similar to B and C is similar to B but A is was not considered similar to C. Therefore A B and C should be considered similar unless otherwise noted.
+ *
+ * @param similarList a list of detected similar images
+ * @return a grouped and combined list of similar images
+ */
+ def processSimilarities(similarList: List[SimilarImages]): List[SimilarImages] = {
+ //process the result into a list we want in cleanedSimilarImages
+ /*
+ Go through all the images. If a similar image for the image doesn't exist, create it. if it does,
+ add it to that similar image. The end result is that all images should be grouped according to
+ their similar images and images that are similar to them.
+ */
+ var count = 0
+ // similar image mapping to map known images back to their similar set
+ val similarImageMap = new mutable.HashMap[Image, SimilarImages]()
+
+ // List of the actual similar image sets
+ val cleanedSimilarImages = new mutable.MutableList[SimilarImages]()
+
+ // loop over all of the similar image sets
+ for (similarImages <- similarList) {
+ count += 1
+ if (count % 25 == 0 || count == similarList.length) {
+ debug(s"Cleaning similar images. $count/${similarList.length} ${similarList.length - count} left to clean")
+ this.searchedListener ! SubmitMessage(s"Cleaning similar images. $count/${similarList.length}")
+ }
+ var foundSimilarity = false
+ var similarity: SimilarImages = null
+ breakable {
+ for (similarImage <- similarImages.similarImages) {
+ if (similarImageMap.contains(similarImage)) {
+ similarity = similarImageMap(similarImage)
+ foundSimilarity = true
+ break()
+ }
+ }
+ }
+
+ //if no similarity was found, one should be created
+ if (!foundSimilarity) {
+ similarity = new SimilarImages(new HashSet[Image])
+ // the created similarity is added to the cleaned list
+ cleanedSimilarImages += similarity
+ }
+
+ // all images should be added to this new similarity
+ similarity.similarImages = similarity.similarImages ++ similarImages.similarImages
+ similarImages.similarImages.foreach(img => similarImageMap.put(img, similarity))
+ }
+
+ //get a count of similar images found
+ var totalCount = 0
+ cleanedSimilarImages.foreach(img => totalCount += img.similarImages.size)
+ debug(s"Found $totalCount images with similarities")
+ this.searchedListener ! SubmitMessage(s"Found $totalCount images with similarities")
+
+ // Sort the similarImages by ?!?!?!? and return
+ cleanedSimilarImages.toList.sorted(SimilarImagesOrdering)
+ }
}
case class SubmitMessage(message: String)
@@ -148,62 +150,62 @@ case class ScannedFileCount(count: Integer, total: Integer, message: String = nu
case class ComparedFileCount(count: Integer, total: Integer, message: String = null)
abstract class EngineListener extends Actor with ActorLogging {
- override def receive: Actor.Receive = {
- case command: SubmitMessage => handleMessage(command)
- case command: ScannedFileCount => handleScannedFileCount(command)
- case command: ComparedFileCount => handleComparedFileCount(command)
- case _ => log.info("received unknown message")
- }
+ override def receive: Actor.Receive = {
+ case command: SubmitMessage => handleMessage(command)
+ case command: ScannedFileCount => handleScannedFileCount(command)
+ case command: ComparedFileCount => handleComparedFileCount(command)
+ case _ => log.info("received unknown message")
+ }
- def handleMessage(command: SubmitMessage)
+ def handleMessage(command: SubmitMessage)
- def handleScannedFileCount(command: ScannedFileCount)
+ def handleScannedFileCount(command: ScannedFileCount)
- def handleComparedFileCount(command: ComparedFileCount)
+ def handleComparedFileCount(command: ComparedFileCount)
}
/**
- * Actor for logging output information
- */
+ * Actor for logging output information
+ */
class DefaultLoggingEngineListener extends EngineListener with ActorLogging {
- override def handleComparedFileCount(command: ComparedFileCount): Unit = {
- if (command.message != null) {
- log.info(command.message)
- }
- log.info("Processed {}/{}", command.count, command.total)
- }
-
- override def handleScannedFileCount(command: ScannedFileCount): Unit = {
- if (command.message != null) {
- log.info(command.message)
- }
- log.info("Scanned {}/{} For Similarities", command.count, command.total)
- }
-
- override def handleMessage(command: SubmitMessage): Unit = {
- log.info(command.message)
- }
+ override def handleComparedFileCount(command: ComparedFileCount): Unit = {
+ if (command.message != null) {
+ log.info(command.message)
+ }
+ log.info("Processed {}/{}", command.count, command.total)
+ }
+
+ override def handleScannedFileCount(command: ScannedFileCount): Unit = {
+ if (command.message != null) {
+ log.info(command.message)
+ }
+ log.info("Scanned {}/{} For Similarities", command.count, command.total)
+ }
+
+ override def handleMessage(command: SubmitMessage): Unit = {
+ log.info(command.message)
+ }
}
/**
- * Actor for writing progress out to the commandline
- */
+ * Actor for writing progress out to the commandline
+ */
class CLIEngineListener extends EngineListener with ActorLogging {
- override def handleComparedFileCount(command: ComparedFileCount): Unit = {
- if (command.message != null) {
- System.out.println(command.message)
- }
- System.out.println(s"Processed ${command.count}/${command.total}")
- }
-
- override def handleScannedFileCount(command: ScannedFileCount): Unit = {
- if (command.message != null) {
- System.out.println(command.message)
- }
- System.out.println(s"Scanned ${command.count}/${command.total} For Similarities")
- }
-
- override def handleMessage(command: SubmitMessage): Unit = {
- System.out.println(command.message)
- }
+ override def handleComparedFileCount(command: ComparedFileCount): Unit = {
+ if (command.message != null) {
+ System.out.println(command.message)
+ }
+ System.out.println(s"Processed ${command.count}/${command.total}")
+ }
+
+ override def handleScannedFileCount(command: ScannedFileCount): Unit = {
+ if (command.message != null) {
+ System.out.println(command.message)
+ }
+ System.out.println(s"Scanned ${command.count}/${command.total} For Similarities")
+ }
+
+ override def handleMessage(command: SubmitMessage): Unit = {
+ System.out.println(command.message)
+ }
}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/SequentialEngine.scala b/engine/src/main/scala/com/sothr/imagetools/engine/SequentialEngine.scala
index a12daa5..90a1805 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/SequentialEngine.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/SequentialEngine.scala
@@ -9,84 +9,84 @@ import grizzled.slf4j.Logging
import scala.collection.mutable
/**
- * Engine that works sequentially
- * Very Slow, but consistent. Excellent for testing
- *
- * Created by drew on 1/26/14.
- */
+ * Engine that works sequentially
+ * Very Slow, but consistent. Excellent for testing
+ *
+ * Created by drew on 1/26/14.
+ */
class SequentialEngine extends Engine with Logging {
- var processedListener = system.actorOf(Props[DefaultLoggingEngineListener],
- name = "ProcessedEngineListener")
- var similarityListener = system.actorOf(Props[DefaultLoggingEngineListener],
- name = "SimilarityEngineListener")
+ var processedListener = system.actorOf(Props[DefaultLoggingEngineListener],
+ name = "ProcessedEngineListener")
+ var similarityListener = system.actorOf(Props[DefaultLoggingEngineListener],
+ name = "SimilarityEngineListener")
- override def setProcessedListener(listenerRef: ActorRef) = {
- this.processedListener = listenerRef
- }
+ override def setProcessedListener(listenerRef: ActorRef) = {
+ this.processedListener = listenerRef
+ }
- override def setSimilarityListener(listenerRef: ActorRef) = {
- this.similarityListener = listenerRef
- }
+ override def setSimilarityListener(listenerRef: ActorRef) = {
+ this.similarityListener = listenerRef
+ }
- def getSimilarImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[SimilarImages] = {
- debug(s"Looking for similar images in directory: $directoryPath")
- val images = getImagesForDirectory(directoryPath, recursive, recursiveDepth)
- info(s"Searching ${images.length} images for similarities")
- val ignoreSet = new mutable.HashSet[Image]()
- val allSimilarImages = new mutable.MutableList[SimilarImages]()
- var processedCount = 0
- var similarCount = 0
- for (rootImage <- images) {
- if (!ignoreSet.contains(rootImage)) {
- if (processedCount % 25 == 0) {
- //info(s"Processed ${processedCount}/${images.length - ignoreSet.size} About ${images.length -
- // processedCount} images to go")
- similarityListener ! ScannedFileCount(processedCount, images.length - ignoreSet.size)
- }
- debug(s"Looking for images similar to: ${rootImage.imagePath}")
- ignoreSet += rootImage
- val similarImages = new mutable.MutableList[Image]()
- for (image <- images) {
- if (!ignoreSet.contains(image)) {
- if (rootImage.isSimilarTo(image)) {
- debug(s"Image: ${image.imagePath} is similar")
- similarImages += image
- ignoreSet += image
- similarCount += 1
- }
- }
- }
- if (similarImages.length > 1) {
- similarImages += rootImage
- val similar = new SimilarImages(similarImages.toSet)
- debug(s"Found similar images: ${similar.toString}")
- allSimilarImages += similar
- }
- processedCount += 1
- }
- }
- info(s"Finished processing ${images.size} images. Found $similarCount similar images")
- this.processSimilarities(allSimilarImages.toList)
- }
+ def getSimilarImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[SimilarImages] = {
+ debug(s"Looking for similar images in directory: $directoryPath")
+ val images = getImagesForDirectory(directoryPath, recursive, recursiveDepth)
+ info(s"Searching ${images.length} images for similarities")
+ val ignoreSet = new mutable.HashSet[Image]()
+ val allSimilarImages = new mutable.MutableList[SimilarImages]()
+ var processedCount = 0
+ var similarCount = 0
+ for (rootImage <- images) {
+ if (!ignoreSet.contains(rootImage)) {
+ if (processedCount % 25 == 0) {
+ //info(s"Processed ${processedCount}/${images.length - ignoreSet.size} About ${images.length -
+ // processedCount} images to go")
+ similarityListener ! ScannedFileCount(processedCount, images.length - ignoreSet.size)
+ }
+ debug(s"Looking for images similar to: ${rootImage.imagePath}")
+ ignoreSet += rootImage
+ val similarImages = new mutable.MutableList[Image]()
+ for (image <- images) {
+ if (!ignoreSet.contains(image)) {
+ if (rootImage.isSimilarTo(image)) {
+ debug(s"Image: ${image.imagePath} is similar")
+ similarImages += image
+ ignoreSet += image
+ similarCount += 1
+ }
+ }
+ }
+ if (similarImages.length > 1) {
+ similarImages += rootImage
+ val similar = new SimilarImages(similarImages.toSet)
+ debug(s"Found similar images: ${similar.toString}")
+ allSimilarImages += similar
+ }
+ processedCount += 1
+ }
+ }
+ info(s"Finished processing ${images.size} images. Found $similarCount similar images")
+ this.processSimilarities(allSimilarImages.toList)
+ }
- def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = {
- debug(s"Looking for images in directory: $directoryPath")
- val images: mutable.MutableList[Image] = new mutable.MutableList[Image]()
- val imageFiles = getAllImageFiles(directoryPath, recursive, recursiveDepth)
- val directory: File = new File(directoryPath)
- var count = 0
- for (file <- imageFiles) {
- count += 1
- if (count % 25 == 0) {
- //info(s"Processed ${count}/${imageFiles.size}")
- processedListener ! ScannedFileCount(count, imageFiles.size)
- }
- val image = ImageService.getImage(file)
- if (image != null) {
- images += image
- }
- }
- images.toList
- }
-}
+ def getImagesForDirectory(directoryPath: String, recursive: Boolean = false, recursiveDepth: Int = 500): List[Image] = {
+ debug(s"Looking for images in directory: $directoryPath")
+ val images: mutable.MutableList[Image] = new mutable.MutableList[Image]()
+ val imageFiles = getAllImageFiles(directoryPath, recursive, recursiveDepth)
+ val directory: File = new File(directoryPath)
+ var count = 0
+ for (file <- imageFiles) {
+ count += 1
+ if (count % 25 == 0) {
+ //info(s"Processed ${count}/${imageFiles.size}")
+ processedListener ! ScannedFileCount(count, imageFiles.size)
+ }
+ val image = ImageService.getImage(file)
+ if (image != null) {
+ images += image
+ }
+ }
+ images.toList
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/dao/HibernateUtil.scala b/engine/src/main/scala/com/sothr/imagetools/engine/dao/HibernateUtil.scala
index f9daab1..df7b502 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/dao/HibernateUtil.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/dao/HibernateUtil.scala
@@ -8,35 +8,33 @@ import org.hibernate.cfg.Configuration
import org.hibernate.service.ServiceRegistry
/**
- * Utility class to interface with hibernate
- *
- * Created by drew on 2/8/14.
- */
+ * Utility class to interface with hibernate
+ *
+ * Created by drew on 2/8/14.
+ */
object HibernateUtil extends Logging {
- private val sessionFactory: SessionFactory = buildSessionFactory()
- private var serviceRegistry: ServiceRegistry = null
+ private val sessionFactory: SessionFactory = buildSessionFactory()
+ private var serviceRegistry: ServiceRegistry = null
- def getSessionFactory: SessionFactory = {
- sessionFactory
- }
+ def getSessionFactory: SessionFactory = {
+ sessionFactory
+ }
- private def buildSessionFactory(): SessionFactory = {
- try {
- // Create the SessionFactory from hibernate.cfg.xml
- val configuration = new Configuration().configure("hibernate.cfg.xml")
- //set the database location
- info(s"Connecting to database at: \'${PropertiesService.get(PropertyEnum.DatabaseConnectionURL.toString)}\'")
- configuration.setProperty("hibernate.connection.url", PropertiesService.get(PropertyEnum.DatabaseConnectionURL.toString))
- serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties).build
- configuration.buildSessionFactory(serviceRegistry)
- } catch {
- case ex: Throwable =>
- // Make sure you log the exception, as it might be swallowed
- error("Initial SessionFactory creation failed.", ex)
- throw new ExceptionInInitializerError(ex)
- }
- }
-
-
-}
+ private def buildSessionFactory(): SessionFactory = {
+ try {
+ // Create the SessionFactory from hibernate.cfg.xml
+ val configuration = new Configuration().configure("hibernate.cfg.xml")
+ //set the database location
+ info(s"Connecting to database at: \'${PropertiesService.get(PropertyEnum.DatabaseConnectionURL.toString)}\'")
+ configuration.setProperty("hibernate.connection.url", PropertiesService.get(PropertyEnum.DatabaseConnectionURL.toString))
+ serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties).build
+ configuration.buildSessionFactory(serviceRegistry)
+ } catch {
+ case ex: Throwable =>
+ // Make sure you log the exception, as it might be swallowed
+ error("Initial SessionFactory creation failed.", ex)
+ throw new ExceptionInInitializerError(ex)
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/dao/ImageDAO.scala b/engine/src/main/scala/com/sothr/imagetools/engine/dao/ImageDAO.scala
index 1a72bb8..0c51fa9 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/dao/ImageDAO.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/dao/ImageDAO.scala
@@ -4,41 +4,40 @@ import com.sothr.imagetools.engine.image.Image
import org.hibernate.{Session, SessionFactory}
/**
- * Interact with stored images
- *
- * Created by drew on 2/8/14.
- */
+ * Interact with stored images
+ *
+ * Created by drew on 2/8/14.
+ */
class ImageDAO {
- private val sessionFactory: SessionFactory = HibernateUtil.getSessionFactory
-
- def find(path: String): Image = {
- val session: Session = sessionFactory.getCurrentSession
- session.getTransaction.begin()
- val result = session.get(classOf[Image], path).asInstanceOf[Image]
- session.getTransaction.commit()
- result
- }
-
- def save(image: Image) = {
- val session: Session = sessionFactory.getCurrentSession
- session.getTransaction.begin()
- session.saveOrUpdate(image)
- session.getTransaction.commit()
- }
-
- def save(images: List[Image]) = {
- val session: Session = sessionFactory.getCurrentSession
- session.getTransaction.begin()
- for (image <- images) session.saveOrUpdate(image)
- session.getTransaction.commit()
- }
-
- def delete(image: Image) = {
- val session: Session = sessionFactory.getCurrentSession
- session.getTransaction.begin()
- session.delete(image)
- session.getTransaction.commit()
- }
-
-}
+ private val sessionFactory: SessionFactory = HibernateUtil.getSessionFactory
+
+ def find(path: String): Image = {
+ val session: Session = sessionFactory.getCurrentSession
+ session.getTransaction.begin()
+ val result = session.get(classOf[Image], path).asInstanceOf[Image]
+ session.getTransaction.commit()
+ result
+ }
+
+ def save(image: Image) = {
+ val session: Session = sessionFactory.getCurrentSession
+ session.getTransaction.begin()
+ session.saveOrUpdate(image)
+ session.getTransaction.commit()
+ }
+
+ def save(images: List[Image]) = {
+ val session: Session = sessionFactory.getCurrentSession
+ session.getTransaction.begin()
+ for (image <- images) session.saveOrUpdate(image)
+ session.getTransaction.commit()
+ }
+
+ def delete(image: Image) = {
+ val session: Session = sessionFactory.getCurrentSession
+ session.getTransaction.begin()
+ session.delete(image)
+ session.getTransaction.commit()
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/image/Image.scala b/engine/src/main/scala/com/sothr/imagetools/engine/image/Image.scala
index de02a54..e0a9e52 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/image/Image.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/image/Image.scala
@@ -11,101 +11,100 @@ import grizzled.slf4j.Logging
@Table(name = "Image")
class Image(val image: String, val thumbnail: String, val size: (Int, Int), val imageHashes: ImageHashVO = null) extends Serializable with Logging {
- @Id
- var imagePath: String = image
- var thumbnailPath: String = thumbnail
- var width: Int = size._1
- var height: Int = size._2
- var hashes: ImageHashVO = imageHashes
- @transient
- var imageSize: (Int, Int) = {
- new Tuple2(width, height)
- }
- @transient
- var imageName: String = ""
- var imageType: ImageType = ImageType.SingleFrameImage
-
- def this() = this("", "", (0, 0), null)
-
- def getThumbnailPath: String = thumbnailPath
-
- def setThumbnailPath(path: String) = {
- thumbnailPath = path
- }
-
- def getWidth: Int = width
-
- def setWidth(size: Int) = {
- width = size
- }
-
- def getHeight: Int = height
-
- def setHeight(size: Int) = {
- height = size
- }
-
- def getHashes: ImageHashVO = hashes
-
- def setHashes(newHashes: ImageHashVO) = {
- hashes = newHashes
- }
-
- def getName: String = {
- if (this.imageName.length < 1) {
- this.imageName = this.getImagePath.split('/').last
- }
- this.imageName
- }
-
- def getImagePath: String = imagePath
-
- def setImagePath(path: String) = {
- imagePath = path
- }
-
- def isSimilarTo(otherImage: Image): Boolean = {
- //debug(s"Checking $imagePath for similarities with ${otherImage.imagePath}")
- HashService.areImageHashesSimilar(PropertiesService.aHashSettings,
- PropertiesService.dHashSettings,
- PropertiesService.pHashSettings,
- ImageService.convertToImageHashDTO(this.hashes),
- ImageService.convertToImageHashDTO(otherImage.hashes))
- }
-
- def getSimilarity(otherImage: Image): Float = {
- HashService.getWeightedHashSimilarity(PropertiesService.aHashSettings,
- PropertiesService.dHashSettings,
- PropertiesService.pHashSettings,
- ImageService.convertToImageHashDTO(this.hashes),
- ImageService.convertToImageHashDTO(otherImage.hashes))
- }
-
- /*def getSimilar(otherImages:Traversable[Image]):Traversable[Image] = {
-
- }*/
-
- def cloneImage: Image = {
- new Image(imagePath, thumbnailPath, imageSize, hashes.cloneHashes)
- }
-
- override def toString: String = {
- s"Image: $imagePath Thumbnail: $thumbnailPath Image Size: ${imageSize._1}x${imageSize._2} Hashes: $hashes"
- }
-
- override def equals(obj: Any) = {
- obj match {
- case that: Image =>
- that.hashCode.equals(this.hashCode)
- case _ => false
- }
- }
-
- override def hashCode: Int = {
- var result = 365
- result = 37 * result + imagePath.hashCode
- result = 41 * result + hashes.hashCode()
- result
- }
-
-}
+ @Id
+ var imagePath: String = image
+ var thumbnailPath: String = thumbnail
+ var width: Int = size._1
+ var height: Int = size._2
+ var hashes: ImageHashVO = imageHashes
+ @transient
+ var imageSize: (Int, Int) = {
+ new Tuple2(width, height)
+ }
+ @transient
+ var imageName: String = ""
+ var imageType: ImageType = ImageType.SingleFrameImage
+
+ def this() = this("", "", (0, 0), null)
+
+ def getThumbnailPath: String = thumbnailPath
+
+ def setThumbnailPath(path: String) = {
+ thumbnailPath = path
+ }
+
+ def getWidth: Int = width
+
+ def setWidth(size: Int) = {
+ width = size
+ }
+
+ def getHeight: Int = height
+
+ def setHeight(size: Int) = {
+ height = size
+ }
+
+ def getHashes: ImageHashVO = hashes
+
+ def setHashes(newHashes: ImageHashVO) = {
+ hashes = newHashes
+ }
+
+ def getName: String = {
+ if (this.imageName.length < 1) {
+ this.imageName = this.getImagePath.split('/').last
+ }
+ this.imageName
+ }
+
+ def getImagePath: String = imagePath
+
+ def setImagePath(path: String) = {
+ imagePath = path
+ }
+
+ def isSimilarTo(otherImage: Image): Boolean = {
+ //debug(s"Checking $imagePath for similarities with ${otherImage.imagePath}")
+ HashService.areImageHashesSimilar(PropertiesService.aHashSettings,
+ PropertiesService.dHashSettings,
+ PropertiesService.pHashSettings,
+ ImageService.convertToImageHashDTO(this.hashes),
+ ImageService.convertToImageHashDTO(otherImage.hashes))
+ }
+
+ def getSimilarity(otherImage: Image): Float = {
+ HashService.getWeightedHashSimilarity(PropertiesService.aHashSettings,
+ PropertiesService.dHashSettings,
+ PropertiesService.pHashSettings,
+ ImageService.convertToImageHashDTO(this.hashes),
+ ImageService.convertToImageHashDTO(otherImage.hashes))
+ }
+
+ /*def getSimilar(otherImages:Traversable[Image]):Traversable[Image] = {
+
+ }*/
+
+ def cloneImage: Image = {
+ new Image(imagePath, thumbnailPath, imageSize, hashes.cloneHashes)
+ }
+
+ override def toString: String = {
+ s"Image: $imagePath Thumbnail: $thumbnailPath Image Size: ${imageSize._1}x${imageSize._2} Hashes: $hashes"
+ }
+
+ override def equals(obj: Any) = {
+ obj match {
+ case that: Image =>
+ that.hashCode.equals(this.hashCode)
+ case _ => false
+ }
+ }
+
+ override def hashCode: Int = {
+ var result = 365
+ result = 37 * result + imagePath.hashCode
+ result = 41 * result + hashes.hashCode()
+ result
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageFilter.scala b/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageFilter.scala
index f5a8fd7..60588d4 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageFilter.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageFilter.scala
@@ -5,19 +5,19 @@ import java.io.{File, FilenameFilter}
import scala.collection.immutable.HashSet
/**
- * Filter for file names
- *
- * Used to detect image files based on extension
- *
- * Created by drew on 1/26/14.
- */
+ * Filter for file names
+ *
+ * Used to detect image files based on extension
+ *
+ * Created by drew on 1/26/14.
+ */
class ImageFilter extends FilenameFilter {
- private val extensions: HashSet[String] = new HashSet[String]() ++ Array("png", "bmp", "gif", "jpg", "jpeg")
+ private val extensions: HashSet[String] = new HashSet[String]() ++ Array("png", "bmp", "gif", "jpg", "jpeg")
- def accept(dir: File, name: String): Boolean = {
- val splitName = name.split('.')
- val extension = if (splitName.length > 1) splitName(splitName.length - 1) else ""
- if (extensions.contains(extension)) true else false
- }
-}
+ def accept(dir: File, name: String): Boolean = {
+ val splitName = name.split('.')
+ val extension = if (splitName.length > 1) splitName(splitName.length - 1) else ""
+ if (extensions.contains(extension)) true else false
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala b/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala
index 7862747..573d89f 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/image/ImageService.scala
@@ -8,148 +8,148 @@ import com.sothr.imagetools.engine.AppConfig
import com.sothr.imagetools.engine.dao.ImageDAO
import com.sothr.imagetools.engine.util.{PropertiesService, PropertyEnum}
import com.sothr.imagetools.engine.vo.ImageHashVO
-import com.sothr.imagetools.hash.{HashService, ImageHash}
import com.sothr.imagetools.hash.util.ImageUtil
+import com.sothr.imagetools.hash.{HashService, ImageHash}
import grizzled.slf4j.Logging
import net.sf.ehcache.Element
import org.hibernate.HibernateException
object ImageService extends Logging {
- val imageCache = AppConfig.cacheManager.getCache("images")
- private val imageDAO = new ImageDAO()
+ val imageCache = AppConfig.cacheManager.getCache("images")
+ private val imageDAO = new ImageDAO()
- def getImage(file: File): Image = {
- try {
- val image = lookupImage(file)
- if (image != null) {
- debug(s"${file.getAbsolutePath} was already processed")
- return image
- } else {
- debug(s"Processing image: ${file.getAbsolutePath}")
- val bufferedImage = ImageIO.read(file)
- val hashes = HashService.getImageHashes(
- PropertiesService.aHashSettings,
- PropertiesService.dHashSettings,
- PropertiesService.pHashSettings,
- bufferedImage,
- file.getAbsolutePath)
+ def getImage(file: File): Image = {
+ try {
+ val image = lookupImage(file)
+ if (image != null) {
+ debug(s"${file.getAbsolutePath} was already processed")
+ return image
+ } else {
+ debug(s"Processing image: ${file.getAbsolutePath}")
+ val bufferedImage = ImageIO.read(file)
+ val hashes = HashService.getImageHashes(
+ PropertiesService.aHashSettings,
+ PropertiesService.dHashSettings,
+ PropertiesService.pHashSettings,
+ bufferedImage,
+ file.getAbsolutePath)
- var thumbnailPath = lookupThumbnailPath(hashes.fileHash)
- if (thumbnailPath == null) thumbnailPath = getThumbnail(bufferedImage, hashes.fileHash)
- val imageSize = {
- (bufferedImage.getWidth, bufferedImage.getHeight)
- }
- val image = new Image(file.getAbsolutePath, thumbnailPath, imageSize, ImageService.convertToImageHashVO(hashes))
- debug(s"Created image: $image")
- return saveImage(image)
- }
- } catch {
- case ioe: IOException => error(s"Error processing ${file.getAbsolutePath}... ${ioe.getMessage}")
- case ex: Exception => error(s"Error processing ${file.getAbsolutePath}... ${ex.getMessage}", ex)
- }
- null
- }
+ var thumbnailPath = lookupThumbnailPath(hashes.fileHash)
+ if (thumbnailPath == null) thumbnailPath = getThumbnail(bufferedImage, hashes.fileHash)
+ val imageSize = {
+ (bufferedImage.getWidth, bufferedImage.getHeight)
+ }
+ val image = new Image(file.getAbsolutePath, thumbnailPath, imageSize, ImageService.convertToImageHashVO(hashes))
+ debug(s"Created image: $image")
+ return saveImage(image)
+ }
+ } catch {
+ case ioe: IOException => error(s"Error processing ${file.getAbsolutePath}... ${ioe.getMessage}")
+ case ex: Exception => error(s"Error processing ${file.getAbsolutePath}... ${ex.getMessage}", ex)
+ }
+ null
+ }
- def convertToImageHashDTO(imageHashVO: ImageHashVO): ImageHash = {
- new ImageHash(imageHashVO.ahash, imageHashVO.dhash, imageHashVO.phash, imageHashVO.fileHash)
- }
+ def convertToImageHashVO(imageHashDTO: ImageHash): ImageHashVO = {
+ new ImageHashVO(imageHashDTO.ahash, imageHashDTO.dhash, imageHashDTO.phash, imageHashDTO.fileHash)
+ }
- def convertToImageHashVO(imageHashDTO: ImageHash): ImageHashVO = {
- new ImageHashVO(imageHashDTO.ahash, imageHashDTO.dhash, imageHashDTO.phash, imageHashDTO.fileHash)
- }
+ private def lookupImage(file: File): Image = {
+ var image: Image = null
+ var found = false
+ //get from memory cache if possible
+ try {
+ if (imageCache.isKeyInCache(file.getAbsolutePath)) {
+ image = imageCache.get(file.getAbsolutePath).getObjectValue.asInstanceOf[Image]
+ found = true
+ }
+ } catch {
+ case npe: NullPointerException => debug(s"\'${file.getAbsolutePath}\' was supposed to be in the cache, but was not")
+ }
+ //get from datastore if possible
+ if (!found) {
+ try {
+ val tempImage = imageDAO.find(file.getAbsolutePath)
+ if (tempImage != null) image = tempImage
+ } catch {
+ case ex: Exception => error(s"Error looking up \'${file.getAbsolutePath}\' was supposed to be in the database, but was not", ex)
+ }
+ }
+ image
+ }
- private def lookupImage(file: File): Image = {
- var image: Image = null
- var found = false
- //get from memory cache if possible
- try {
- if (imageCache.isKeyInCache(file.getAbsolutePath)) {
- image = imageCache.get(file.getAbsolutePath).getObjectValue.asInstanceOf[Image]
- found = true
- }
- } catch {
- case npe: NullPointerException => debug(s"\'${file.getAbsolutePath}\' was supposed to be in the cache, but was not")
- }
- //get from datastore if possible
- if (!found) {
- try {
- val tempImage = imageDAO.find(file.getAbsolutePath)
- if (tempImage != null) image = tempImage
- } catch {
- case ex: Exception => error(s"Error looking up \'${file.getAbsolutePath}\' was supposed to be in the database, but was not", ex)
- }
- }
- image
- }
+ private def saveImage(image: Image): Image = {
+ //save to cache
+ imageCache.put(new Element(image.imagePath, image))
+ //save to datastore
+ try {
+ imageDAO.save(image)
+ } catch {
+ case ex: Exception => error(s"Error saving \'${image.imagePath}\' to database", ex)
+ }
+ image
+ }
- private def saveImage(image: Image): Image = {
- //save to cache
- imageCache.put(new Element(image.imagePath, image))
- //save to datastore
- try {
- imageDAO.save(image)
- } catch {
- case ex: Exception => error(s"Error saving \'${image.imagePath}\' to database", ex)
- }
- image
- }
+ def lookupThumbnailPath(md5: String): String = {
+ var thumbPath: String = null
+ if (md5 != null) {
+ //check for the actual file
+ val checkPath = calculateThumbPath(md5)
+ if (new File(checkPath).exists) thumbPath = checkPath
+ } else {
+ error("Null md5 passed in")
+ }
+ thumbPath
+ }
- def lookupThumbnailPath(md5: String): String = {
- var thumbPath: String = null
- if (md5 != null) {
- //check for the actual file
- val checkPath = calculateThumbPath(md5)
- if (new File(checkPath).exists) thumbPath = checkPath
- } else {
- error("Null md5 passed in")
- }
- thumbPath
- }
+ def calculateThumbPath(md5: String): String = {
+ //break the path down into 4 char parts
+ val subPath = md5.substring(0, 3)
+ var path: String = s"${PropertiesService.get(PropertyEnum.ThumbnailDirectory.toString)}${PropertiesService.get(PropertyEnum.ThumbnailSize.toString)}/$subPath/"
+ try {
+ val dir = new File(path)
+ if (!dir.exists()) dir.mkdirs()
+ } catch {
+ case ioe: IOException => error(s"Unable to create dirs for path: \'$path\'", ioe)
+ }
+ path += md5 + ".jpg"
+ path
+ }
- def calculateThumbPath(md5: String): String = {
- //break the path down into 4 char parts
- val subPath = md5.substring(0, 3)
- var path: String = s"${PropertiesService.get(PropertyEnum.ThumbnailDirectory.toString)}${PropertiesService.get(PropertyEnum.ThumbnailSize.toString)}/$subPath/"
- try {
- val dir = new File(path)
- if (!dir.exists()) dir.mkdirs()
- } catch {
- case ioe: IOException => error(s"Unable to create dirs for path: \'$path\'", ioe)
- }
- path += md5 + ".jpg"
- path
- }
+ def getThumbnail(image: BufferedImage, md5: String): String = {
+ //create thumbnail
+ val thumb = ImageUtil.resize(image, PropertiesService.get(PropertyEnum.ThumbnailSize.toString).toInt, forced = false)
+ //calculate path
+ val path = calculateThumbPath(md5)
+ // save thumbnail to path
+ try {
+ ImageIO.write(thumb, "png", new File(path))
+ debug(s"Wrote thumbnail to $path")
+ } catch {
+ case ioe: IOException => error(s"Unable to save thumbnail to $path", ioe)
+ }
+ // return path
+ path
+ }
- def getThumbnail(image: BufferedImage, md5: String): String = {
- //create thumbnail
- val thumb = ImageUtil.resize(image, PropertiesService.get(PropertyEnum.ThumbnailSize.toString).toInt, forced = false)
- //calculate path
- val path = calculateThumbPath(md5)
- // save thumbnail to path
- try {
- ImageIO.write(thumb, "png", new File(path))
- debug(s"Wrote thumbnail to $path")
- } catch {
- case ioe: IOException => error(s"Unable to save thumbnail to $path", ioe)
- }
- // return path
- path
- }
+ def convertToImageHashDTO(imageHashVO: ImageHashVO): ImageHash = {
+ new ImageHash(imageHashVO.ahash, imageHashVO.dhash, imageHashVO.phash, imageHashVO.fileHash)
+ }
- def deleteImage(image: Image) = {
- debug(s"Attempting to delete all traces of image: ${image.getImagePath}")
- try {
- val imageFile = new File(image.imagePath)
- //try to delete the file
- imageFile.delete()
- //purge the file from the database and cache
- this.imageCache.remove(imageFile.getAbsolutePath)
- this.imageDAO.delete(image)
- } catch {
- case se: SecurityException => error(s"Unable to delete file: ${image.getImagePath} due to a security exception", se)
- case ise: IllegalStateException => error(s"Unable to delete file: ${image.getImagePath} due to an illegal state exception", ise)
- case he: HibernateException => error(s"Unable to delete file: ${image.getImagePath} due to a hibernate exception", he)
- }
- }
-}
+ def deleteImage(image: Image) = {
+ debug(s"Attempting to delete all traces of image: ${image.getImagePath}")
+ try {
+ val imageFile = new File(image.imagePath)
+ //try to delete the file
+ imageFile.delete()
+ //purge the file from the database and cache
+ this.imageCache.remove(imageFile.getAbsolutePath)
+ this.imageDAO.delete(image)
+ } catch {
+ case se: SecurityException => error(s"Unable to delete file: ${image.getImagePath} due to a security exception", se)
+ case ise: IllegalStateException => error(s"Unable to delete file: ${image.getImagePath} due to an illegal state exception", ise)
+ case he: HibernateException => error(s"Unable to delete file: ${image.getImagePath} due to a hibernate exception", he)
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/image/SimilarImages.scala b/engine/src/main/scala/com/sothr/imagetools/engine/image/SimilarImages.scala
index 86a75dc..7aa19c0 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/image/SimilarImages.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/image/SimilarImages.scala
@@ -3,41 +3,41 @@ package com.sothr.imagetools.engine.image
import grizzled.slf4j.Logging
/**
- * Similar Image payload class
- *
- * Created by drew on 1/26/14.
- */
+ * Similar Image payload class
+ *
+ * Created by drew on 1/26/14.
+ */
class SimilarImages(var similarImages: Set[Image]) extends Logging {
- override def hashCode: Int = {
- val prime = 7
- var result = prime * similarImages.size
- for (similarImage <- similarImages) {
- result = prime * result + similarImage.hashCode
- }
- result
- }
-
- override def toString: String = {
- s"""Similar Images:
+ override def hashCode: Int = {
+ val prime = 7
+ var result = prime * similarImages.size
+ for (similarImage <- similarImages) {
+ result = prime * result + similarImage.hashCode
+ }
+ result
+ }
+
+ override def toString: String = {
+ s"""Similar Images:
$getPrettySimilarImagesList""".stripMargin
- }
+ }
- protected def getPrettySimilarImagesList: String = {
- val sb = new StringBuilder()
- for (image <- similarImages) {
- sb.append(image.imagePath)
- sb.append(System.lineSeparator())
- }
- sb.toString()
- }
+ protected def getPrettySimilarImagesList: String = {
+ val sb = new StringBuilder()
+ for (image <- similarImages) {
+ sb.append(image.imagePath)
+ sb.append(System.lineSeparator())
+ }
+ sb.toString()
+ }
- def ordering() = {
- 1 * similarImages.size
- }
+ def ordering() = {
+ 1 * similarImages.size
+ }
}
object SimilarImagesOrdering extends Ordering[SimilarImages] {
- def compare(a:SimilarImages, b:SimilarImages) = a.ordering() compare b.ordering()
-}
+ def compare(a: SimilarImages, b: SimilarImages) = a.ordering() compare b.ordering()
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/DirectoryFilter.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/DirectoryFilter.scala
index 66e25fd..9d4a04c 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/util/DirectoryFilter.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/DirectoryFilter.scala
@@ -3,13 +3,13 @@ package com.sothr.imagetools.engine.util
import java.io.{File, FilenameFilter}
/**
- * Filter directories
- *
- * Created by drew on 1/26/14.
- */
+ * Filter directories
+ *
+ * Created by drew on 1/26/14.
+ */
class DirectoryFilter extends FilenameFilter {
- def accept(dir: File, name: String): Boolean = {
- new File(dir, name).isDirectory
- }
-}
+ def accept(dir: File, name: String): Boolean = {
+ new File(dir, name).isDirectory
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/Hamming.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/Hamming.scala
index 466ed51..87a45f5 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/util/Hamming.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/Hamming.scala
@@ -2,17 +2,17 @@ package com.sothr.imagetools.engine.util
object Hamming {
- /**
- * Calculate the hamming distance between two longs
- *
- * @param hash1 The first hash to compare
- * @param hash2 The second hash to compare
- * @return
- */
- def getDistance(hash1: Long, hash2: Long): Int = {
- //The XOR of hash1 and hash2 is converted to a binary string
- //then the number of '1's is counted. This is the hamming distance
- (hash1 ^ hash2).toBinaryString.count(_ == '1')
- }
+ /**
+ * Calculate the hamming distance between two longs
+ *
+ * @param hash1 The first hash to compare
+ * @param hash2 The second hash to compare
+ * @return
+ */
+ def getDistance(hash1: Long, hash2: Long): Int = {
+ //The XOR of hash1 and hash2 is converted to a binary string
+ //then the number of '1's is counted. This is the hamming distance
+ (hash1 ^ hash2).toBinaryString.count(_ == '1')
+ }
}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala
index 65cdfa9..a588e52 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertiesService.scala
@@ -12,118 +12,118 @@ import grizzled.slf4j.Logging
*/
object PropertiesService extends Logging {
- //OS information
- val OS: String = System.getProperty("os.name", "UNKNOWN")
- val OS_VERSION: String = System.getProperty("os.version", "UNKNOWN")
- val OS_ARCH: String = System.getProperty("os.arch", "UNKNOWN")
- private val newUserConf: Properties = new Properties()
- private val configRenderOptions = ConfigRenderOptions.concise().setFormatted(true)
- //specific highly used properties
- var TimingEnabled: Boolean = false
- var aHashSettings = new HashSetting("AHash", false, 0, 0, 0.0f)
- var dHashSettings = new HashSetting("DHash", false, 0, 0, 0.0f)
- var pHashSettings = new HashSetting("PHash", false, 0, 0, 0.0f)
- private var defaultConf: Config = _
- private var userConf: Config = _
- private var version: Version = _
-
- def getVersion: Version = this.version
-
- /*
- * Load the properties file from the specified location
- */
- def loadProperties(defaultLocation: String, userLocation: String = null) = {
- info(s"Attempting to load properties from: $defaultLocation")
- defaultConf = ConfigFactory.load(defaultLocation)
- if (userLocation != null) {
- userConf = ConfigFactory.parseFile(new File(userLocation))
- } else {
- userConf = ConfigFactory.empty
- info("No user properties file exists to load from")
- }
- version = new Version(get(PropertyEnum.Version.toString))
- info(s"Detected Version: $version")
-
- //load special properties
- TimingEnabled = get(PropertyEnum.Timed.toString).toBoolean
-
- aHashSettings = new HashSetting(
- "AHash",
- get(PropertyEnum.UseAhash.toString).toBoolean,
- get(PropertyEnum.AhashPrecision.toString).toInt,
- get(PropertyEnum.AhashTolerance.toString).toInt,
- get(PropertyEnum.AhashWeight.toString).toFloat
- )
-
- dHashSettings = new HashSetting(
- "DHash",
- get(PropertyEnum.UseDhash.toString).toBoolean,
- get(PropertyEnum.DhashPrecision.toString).toInt,
- get(PropertyEnum.DhashTolerance.toString).toInt,
- get(PropertyEnum.DhashWeight.toString).toFloat
- )
-
- pHashSettings = new HashSetting(
- "PHash",
- get(PropertyEnum.UsePhash.toString).toBoolean,
- get(PropertyEnum.PhashPrecision.toString).toInt,
- get(PropertyEnum.PhashTolerance.toString).toInt,
- get(PropertyEnum.PhashWeight.toString).toFloat
- )
- info("Loaded Special Properties")
- }
-
- def get(key: String, defaultValue: String = null): String = {
- var result: String = defaultValue
- //check the latest properties
- if (newUserConf.containsKey(key)) {
- result = newUserConf.getProperty(key)
- }
- //check the loaded user properties
- else if (userConf.hasPath(key)) {
- result = userConf.getString(key)
- }
- //check the default properties
- else if (defaultConf.hasPath(key)) {
- result = defaultConf.getString(key)
- }
- result
- }
-
- def saveConf(location: String) = {
- info(s"Saving user properties to $location")
- val out: PrintStream = new PrintStream(new FileOutputStream(location, false))
- val userConfToSave = getCleanedMergedUserConf
- //print to the output stream
- out.print(userConfToSave.root().render(configRenderOptions))
- out.flush()
- out.close()
- }
-
- private def getCleanedMergedUserConf: Config = {
- ConfigFactory.parseProperties(cleanAndPrepareNewUserProperties()) withFallback userConf
- }
-
- private def cleanAndPrepareNewUserProperties(): Properties = {
- //insert special keys here
- newUserConf.setProperty(PropertyEnum.PreviousVersion.toString, version.parsableToString())
- //remove special keys here
- newUserConf.remove(PropertyEnum.Version.toString)
- newUserConf
- }
-
- def has(key: String): Boolean = {
- var result = false
- if (newUserConf.containsKey(key)
- || userConf.hasPath(key)
- || defaultConf.hasPath(key)) {
- result = true
- }
- result
- }
-
- def set(key: String, value: String) = {
- newUserConf.setProperty(key, value)
- }
+ //OS information
+ val OS: String = System.getProperty("os.name", "UNKNOWN")
+ val OS_VERSION: String = System.getProperty("os.version", "UNKNOWN")
+ val OS_ARCH: String = System.getProperty("os.arch", "UNKNOWN")
+ private val newUserConf: Properties = new Properties()
+ private val configRenderOptions = ConfigRenderOptions.concise().setFormatted(true)
+ //specific highly used properties
+ var TimingEnabled: Boolean = false
+ var aHashSettings = new HashSetting("AHash", false, 0, 0, 0.0f)
+ var dHashSettings = new HashSetting("DHash", false, 0, 0, 0.0f)
+ var pHashSettings = new HashSetting("PHash", false, 0, 0, 0.0f)
+ private var defaultConf: Config = _
+ private var userConf: Config = _
+ private var version: Version = _
+
+ def getVersion: Version = this.version
+
+ /*
+ * Load the properties file from the specified location
+ */
+ def loadProperties(defaultLocation: String, userLocation: String = null) = {
+ info(s"Attempting to load properties from: $defaultLocation")
+ defaultConf = ConfigFactory.load(defaultLocation)
+ if (userLocation != null) {
+ userConf = ConfigFactory.parseFile(new File(userLocation))
+ } else {
+ userConf = ConfigFactory.empty
+ info("No user properties file exists to load from")
+ }
+ version = new Version(get(PropertyEnum.Version.toString))
+ info(s"Detected Version: $version")
+
+ //load special properties
+ TimingEnabled = get(PropertyEnum.Timed.toString).toBoolean
+
+ aHashSettings = new HashSetting(
+ "AHash",
+ get(PropertyEnum.UseAhash.toString).toBoolean,
+ get(PropertyEnum.AhashPrecision.toString).toInt,
+ get(PropertyEnum.AhashTolerance.toString).toInt,
+ get(PropertyEnum.AhashWeight.toString).toFloat
+ )
+
+ dHashSettings = new HashSetting(
+ "DHash",
+ get(PropertyEnum.UseDhash.toString).toBoolean,
+ get(PropertyEnum.DhashPrecision.toString).toInt,
+ get(PropertyEnum.DhashTolerance.toString).toInt,
+ get(PropertyEnum.DhashWeight.toString).toFloat
+ )
+
+ pHashSettings = new HashSetting(
+ "PHash",
+ get(PropertyEnum.UsePhash.toString).toBoolean,
+ get(PropertyEnum.PhashPrecision.toString).toInt,
+ get(PropertyEnum.PhashTolerance.toString).toInt,
+ get(PropertyEnum.PhashWeight.toString).toFloat
+ )
+ info("Loaded Special Properties")
+ }
+
+ def get(key: String, defaultValue: String = null): String = {
+ var result: String = defaultValue
+ //check the latest properties
+ if (newUserConf.containsKey(key)) {
+ result = newUserConf.getProperty(key)
+ }
+ //check the loaded user properties
+ else if (userConf.hasPath(key)) {
+ result = userConf.getString(key)
+ }
+ //check the default properties
+ else if (defaultConf.hasPath(key)) {
+ result = defaultConf.getString(key)
+ }
+ result
+ }
+
+ def saveConf(location: String) = {
+ info(s"Saving user properties to $location")
+ val out: PrintStream = new PrintStream(new FileOutputStream(location, false))
+ val userConfToSave = getCleanedMergedUserConf
+ //print to the output stream
+ out.print(userConfToSave.root().render(configRenderOptions))
+ out.flush()
+ out.close()
+ }
+
+ private def getCleanedMergedUserConf: Config = {
+ ConfigFactory.parseProperties(cleanAndPrepareNewUserProperties()) withFallback userConf
+ }
+
+ private def cleanAndPrepareNewUserProperties(): Properties = {
+ //insert special keys here
+ newUserConf.setProperty(PropertyEnum.PreviousVersion.toString, version.parsableToString())
+ //remove special keys here
+ newUserConf.remove(PropertyEnum.Version.toString)
+ newUserConf
+ }
+
+ def has(key: String): Boolean = {
+ var result = false
+ if (newUserConf.containsKey(key)
+ || userConf.hasPath(key)
+ || defaultConf.hasPath(key)) {
+ result = true
+ }
+ result
+ }
+
+ def set(key: String, value: String) = {
+ newUserConf.setProperty(key, value)
+ }
}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertyEnum.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertyEnum.scala
index ef53c34..6b71879 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertyEnum.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/PropertyEnum.scala
@@ -1,32 +1,32 @@
package com.sothr.imagetools.engine.util
object PropertyEnum extends Enumeration {
- type PropertiesEnum = Value
- val Version = Value("app.version.current")
- val PreviousVersion = Value("app.version.previous")
- //default app settings
- val Timed = Value("app.timed")
- //default engine concurrency settings
- val ConcurrentSimilarityLimit = Value("app.engine.concurrent.similarity.limit")
- val ConcurrentProcessingLimit = Value("app.engine.concurrent.processing.limit")
- //default image settings
- val ImageDifferenceThreshold = Value("app.image.differenceThreshold")
- val HashPrecision = Value("app.image.hash.precision")
- val UseAhash = Value("app.image.ahash.use")
- val AhashWeight = Value("app.image.ahash.weight")
- val AhashPrecision = Value("app.image.ahash.precision")
- val AhashTolerance = Value("app.image.ahash.tolerance")
- val UseDhash = Value("app.image.dhash.use")
- val DhashWeight = Value("app.image.dhash.weight")
- val DhashPrecision = Value("app.image.dhash.precision")
- val DhashTolerance = Value("app.image.dhash.tolerance")
- val UsePhash = Value("app.image.phash.use")
- val PhashWeight = Value("app.image.phash.weight")
- val PhashPrecision = Value("app.image.phash.precision")
- val PhashTolerance = Value("app.image.phash.tolerance")
- //Default Thumbnail Settings
- val ThumbnailDirectory = Value("app.thumbnail.directory")
- val ThumbnailSize = Value("app.thumbnail.size")
- //Default Database Settings
- val DatabaseConnectionURL = Value("app.database.connectionURL")
+ type PropertiesEnum = Value
+ val Version = Value("app.version.current")
+ val PreviousVersion = Value("app.version.previous")
+ //default app settings
+ val Timed = Value("app.timed")
+ //default engine concurrency settings
+ val ConcurrentSimilarityLimit = Value("app.engine.concurrent.similarity.limit")
+ val ConcurrentProcessingLimit = Value("app.engine.concurrent.processing.limit")
+ //default image settings
+ val ImageDifferenceThreshold = Value("app.image.differenceThreshold")
+ val HashPrecision = Value("app.image.hash.precision")
+ val UseAhash = Value("app.image.ahash.use")
+ val AhashWeight = Value("app.image.ahash.weight")
+ val AhashPrecision = Value("app.image.ahash.precision")
+ val AhashTolerance = Value("app.image.ahash.tolerance")
+ val UseDhash = Value("app.image.dhash.use")
+ val DhashWeight = Value("app.image.dhash.weight")
+ val DhashPrecision = Value("app.image.dhash.precision")
+ val DhashTolerance = Value("app.image.dhash.tolerance")
+ val UsePhash = Value("app.image.phash.use")
+ val PhashWeight = Value("app.image.phash.weight")
+ val PhashPrecision = Value("app.image.phash.precision")
+ val PhashTolerance = Value("app.image.phash.tolerance")
+ //Default Thumbnail Settings
+ val ThumbnailDirectory = Value("app.thumbnail.directory")
+ val ThumbnailSize = Value("app.thumbnail.size")
+ //Default Database Settings
+ val DatabaseConnectionURL = Value("app.database.connectionURL")
}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/Timing.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/Timing.scala
index 041a8d0..67e860e 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/util/Timing.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/Timing.scala
@@ -4,32 +4,31 @@ import grizzled.slf4j.Logging
trait Timing extends Logging {
- def time[R](block: => R): R = {
- val t0 = System.currentTimeMillis
- val result = block // call-by-name
- val t1 = System.currentTimeMillis
- debug("Elapsed time: " + (t1 - t0) + "ms")
- result
- }
+ def time[R](block: => R): R = {
+ val t0 = System.currentTimeMillis
+ val result = block // call-by-name
+ val t1 = System.currentTimeMillis
+ debug("Elapsed time: " + (t1 - t0) + "ms")
+ result
+ }
- def getTime[R](block: => R): Long = {
- val t0 = System.currentTimeMillis
- val result = block // call-by-name
- val t1 = System.currentTimeMillis
- debug("Elapsed time: " + (t1 - t0) + "ms")
- t1 - t0
- }
+ def getTime[R](block: => R): Long = {
+ val t0 = System.currentTimeMillis
+ val result = block // call-by-name
+ val t1 = System.currentTimeMillis
+ debug("Elapsed time: " + (t1 - t0) + "ms")
+ t1 - t0
+ }
- def getMean(times: Long*): Long = {
- getMean(times.toArray[Long])
- }
-
- def getMean(times: Array[Long]): Long = {
- var ag = 0L
- for (i <- times.indices) {
- ag += times(i)
- }
- ag / times.length
- }
+ def getMean(times: Long*): Long = {
+ getMean(times.toArray[Long])
+ }
+ def getMean(times: Array[Long]): Long = {
+ var ag = 0L
+ for (i <- times.indices) {
+ ag += times(i)
+ }
+ ag / times.length
+ }
}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/util/Version.scala b/engine/src/main/scala/com/sothr/imagetools/engine/util/Version.scala
index 06a2d64..5e1a8ef 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/util/Version.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/util/Version.scala
@@ -3,90 +3,90 @@ package com.sothr.imagetools.engine.util
import grizzled.slf4j.Logging
/**
- * Class to handle version detection and evaluation
- *
- * Created by drew on 1/6/14.
- */
+ * Class to handle version detection and evaluation
+ *
+ * Created by drew on 1/6/14.
+ */
class Version(val versionString: String) extends Logging {
- //parse version into parts
- //typical version string i.e. 0.1.0-DEV-27-060aec7
- val (major, minor, patch, buildTag, buildNumber, buildHash) = {
- var version: (Int, Int, Int, String, Int, String) = (0, 0, 0, "DEV", 0, "asdfzxcv")
- try {
- val splitVersion = versionString.split( """\.""")
- val splitType = splitVersion(splitVersion.length - 1).split( """-""")
- version = (splitVersion(0).toInt, splitVersion(1).toInt, splitType(0).toInt, splitType(1), splitType(2).toInt, splitType(3))
- } catch {
- case nfe: NumberFormatException => error(s"Error parsing number from version string '$versionString'", nfe)
- case e: Exception => error(s"Unexpected error parsing version string '$versionString'", e)
- }
- version
- }
+ //parse version into parts
+ //typical version string i.e. 0.1.0-DEV-27-060aec7
+ val (major, minor, patch, buildTag, buildNumber, buildHash) = {
+ var version: (Int, Int, Int, String, Int, String) = (0, 0, 0, "DEV", 0, "asdfzxcv")
+ try {
+ val splitVersion = versionString.split( """\.""")
+ val splitType = splitVersion(splitVersion.length - 1).split( """-""")
+ version = (splitVersion(0).toInt, splitVersion(1).toInt, splitType(0).toInt, splitType(1), splitType(2).toInt, splitType(3))
+ } catch {
+ case nfe: NumberFormatException => error(s"Error parsing number from version string '$versionString'", nfe)
+ case e: Exception => error(s"Unexpected error parsing version string '$versionString'", e)
+ }
+ version
+ }
- /*
- * -3 = this.patch < that.patch
- * -2 = this.minor < that.minor
- * -1 = this.major < that.major
- * 0 = Identical Versions
- * 1 = this.major > that.major
- * 2 = this.minor > that.minor
- * 3 = this.patch > that.patch
- * 4 = this.buildTag != that.buildTag
- */
- def compare(that: Version): Integer = {
- //Identical Versions
- if (this.hashCode == that.hashCode) {
- 0
- // This is at least a major version ahead
- } else if (this.major > that.major) {
- 1
- // This is at least a major version behind
- } else if (this.major < that.major) {
- -1
- // major is the same
- } else {
- // This is at least a minor version ahead
- if (this.minor > that.minor) {
- 2
- // This is at least a minor version behind
- } else if (this.minor < that.minor) {
- -2
- // major.minor are the same
- } else {
- // This is at least a patch version ahead
- if (this.patch > that.patch) {
- 3
- // This is at least a patch version version
- } else if (this.patch < that.patch) {
- -3
- //major.minor.patch are all the same
- } else {
- // This is a different build
- if (this.buildTag != that.buildTag) {
- 4
- }
- //should be caught by the first if, but in case not
- 0
- }
- }
- }
- }
+ /*
+ * -3 = this.patch < that.patch
+ * -2 = this.minor < that.minor
+ * -1 = this.major < that.major
+ * 0 = Identical Versions
+ * 1 = this.major > that.major
+ * 2 = this.minor > that.minor
+ * 3 = this.patch > that.patch
+ * 4 = this.buildTag != that.buildTag
+ */
+ def compare(that: Version): Integer = {
+ //Identical Versions
+ if (this.hashCode == that.hashCode) {
+ 0
+ // This is at least a major version ahead
+ } else if (this.major > that.major) {
+ 1
+ // This is at least a major version behind
+ } else if (this.major < that.major) {
+ -1
+ // major is the same
+ } else {
+ // This is at least a minor version ahead
+ if (this.minor > that.minor) {
+ 2
+ // This is at least a minor version behind
+ } else if (this.minor < that.minor) {
+ -2
+ // major.minor are the same
+ } else {
+ // This is at least a patch version ahead
+ if (this.patch > that.patch) {
+ 3
+ // This is at least a patch version version
+ } else if (this.patch < that.patch) {
+ -3
+ //major.minor.patch are all the same
+ } else {
+ // This is a different build
+ if (this.buildTag != that.buildTag) {
+ 4
+ }
+ //should be caught by the first if, but in case not
+ 0
+ }
+ }
+ }
+ }
- override def hashCode(): Int = {
- val prime: Int = 37
- val result: Int = 255
- var hash: Int = major
- hash += minor
- hash += patch
- hash += buildTag.hashCode
- prime * result + hash
- }
+ override def hashCode(): Int = {
+ val prime: Int = 37
+ val result: Int = 255
+ var hash: Int = major
+ hash += minor
+ hash += patch
+ hash += buildTag.hashCode
+ prime * result + hash
+ }
- def parsableToString(): String = {
- s"$major.$minor.$patch-$buildTag-$buildNumber-$buildHash"
- }
+ def parsableToString(): String = {
+ s"$major.$minor.$patch-$buildTag-$buildNumber-$buildHash"
+ }
- override def toString: String = {
- s"$major.$minor.$patch-$buildTag build:$buildNumber code:$buildHash"
- }
-}
+ override def toString: String = {
+ s"$major.$minor.$patch-$buildTag build:$buildNumber code:$buildHash"
+ }
+}
\ No newline at end of file
diff --git a/engine/src/main/scala/com/sothr/imagetools/engine/vo/ImageHashVO.scala b/engine/src/main/scala/com/sothr/imagetools/engine/vo/ImageHashVO.scala
index d514b5c..2aeecad 100644
--- a/engine/src/main/scala/com/sothr/imagetools/engine/vo/ImageHashVO.scala
+++ b/engine/src/main/scala/com/sothr/imagetools/engine/vo/ImageHashVO.scala
@@ -8,55 +8,55 @@ import grizzled.slf4j.Logging
@Table(name = "ImageHash")
class ImageHashVO(var ahash: Long, var dhash: Long, var phash: Long, var fileHash: String) extends Serializable with Logging {
- @Id
- @GeneratedValue(strategy = GenerationType.AUTO)
- var id: Int = _
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ var id: Int = _
- def this() = this(0l, 0l, 0l, "")
+ def this() = this(0l, 0l, 0l, "")
- def getId: Int = id
+ def getId: Int = id
- def setId(newId: Int) = {
- id = newId
- }
+ def setId(newId: Int) = {
+ id = newId
+ }
- def getAhash: Long = ahash
+ def getAhash: Long = ahash
- def setAhash(hash: Long) = {
- ahash = hash
- }
+ def setAhash(hash: Long) = {
+ ahash = hash
+ }
- def getDhash: Long = dhash
+ def getDhash: Long = dhash
- def setDhash(hash: Long) = {
- dhash = hash
- }
+ def setDhash(hash: Long) = {
+ dhash = hash
+ }
- def getPhash: Long = phash
+ def getPhash: Long = phash
- def setPhash(hash: Long) = {
- phash = hash
- }
+ def setPhash(hash: Long) = {
+ phash = hash
+ }
- def getFileHash: String = fileHash
+ def getFileHash: String = fileHash
- def setFileHash(hash: String) = {
- fileHash = hash
- }
+ def setFileHash(hash: String) = {
+ fileHash = hash
+ }
- def cloneHashes: ImageHashVO = {
- new ImageHashVO(ahash, dhash, phash, fileHash)
- }
+ def cloneHashes: ImageHashVO = {
+ new ImageHashVO(ahash, dhash, phash, fileHash)
+ }
- override def hashCode(): Int = {
- var result = 365
- result = 41 * result + (this.ahash ^ (this.ahash >>> 32)).toInt
- result = 37 * result + (this.dhash ^ (this.dhash >>> 32)).toInt
- result = 2 * result + (this.phash ^ (this.phash >>> 32)).toInt
- result
- }
+ override def hashCode(): Int = {
+ var result = 365
+ result = 41 * result + (this.ahash ^ (this.ahash >>> 32)).toInt
+ result = 37 * result + (this.dhash ^ (this.dhash >>> 32)).toInt
+ result = 2 * result + (this.phash ^ (this.phash >>> 32)).toInt
+ result
+ }
- override def toString: String = {
- s"fileHash: $fileHash ahash: $ahash dhash: $dhash phash: $phash"
- }
-}
+ override def toString: String = {
+ s"fileHash: $fileHash ahash: $ahash dhash: $dhash phash: $phash"
+ }
+}
\ No newline at end of file
diff --git a/engine/src/test/java/com/sothr/imagetools/engine/AppTest.java b/engine/src/test/java/com/sothr/imagetools/engine/AppTest.java
index 2a3cdba..5b41cda 100644
--- a/engine/src/test/java/com/sothr/imagetools/engine/AppTest.java
+++ b/engine/src/test/java/com/sothr/imagetools/engine/AppTest.java
@@ -8,26 +8,26 @@ import junit.framework.TestSuite;
* Unit test for simple App.
*/
public class AppTest extends TestCase {
- /**
- * Create the test case
- *
- * @param testName name of the test case
- */
- public AppTest(String testName) {
- super(testName);
- }
+ /**
+ * Create the test case
+ *
+ * @param testName name of the test case
+ */
+ public AppTest(String testName) {
+ super(testName);
+ }
- /**
- * @return the suite of tests being tested
- */
- public static Test suite() {
- return new TestSuite(AppTest.class);
- }
+ /**
+ * @return the suite of tests being tested
+ */
+ public static Test suite() {
+ return new TestSuite(AppTest.class);
+ }
- /**
- * Rigourous Test :-)
- */
- public void testApp() {
- assertTrue(true);
- }
-}
+ /**
+ * Rigourous Test :-)
+ */
+ public void testApp() {
+ assertTrue(true);
+ }
+}
\ No newline at end of file
diff --git a/engine/src/test/resources/ehcache.xml b/engine/src/test/resources/ehcache.xml
index 1e29df4..b7b28e4 100644
--- a/engine/src/test/resources/ehcache.xml
+++ b/engine/src/test/resources/ehcache.xml
@@ -1,8 +1,8 @@
-
-
-
-
+
+
+
+
diff --git a/engine/src/test/resources/hibernate.cfg.xml b/engine/src/test/resources/hibernate.cfg.xml
index a1a6f1c..cc413a8 100644
--- a/engine/src/test/resources/hibernate.cfg.xml
+++ b/engine/src/test/resources/hibernate.cfg.xml
@@ -1,48 +1,48 @@
+ "-//Hibernate/Hibernate Configuration DTD//EN"
+ "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
-
-
+
+
-
- org.h2.Driver
- org.hibernate.dialect.H2Dialect
- true
-
- java:comp/UserTransaction
+
+ org.h2.Driver
+ org.hibernate.dialect.H2Dialect
+ true
+
+ java:comp/UserTransaction
- create
+ create
-
- thread
+
+ thread
-
- org.hibernate.cache.ehcache.EhCacheRegionFactory
-
- org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
-
- true
+
+ org.hibernate.cache.ehcache.EhCacheRegionFactory
+
+ org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
+
+ true
- 1
- 100
-
- 50
- 0
- 5
- 100
-
+ 1
+ 100
+
+ 50
+ 0
+ 5
+ 100
+
-
-
-
+
+
+
-
-
-
+
+
+
\ No newline at end of file
diff --git a/engine/src/test/resources/hibernate/Image.hbm.xml b/engine/src/test/resources/hibernate/Image.hbm.xml
index bbe679b..c662c7a 100644
--- a/engine/src/test/resources/hibernate/Image.hbm.xml
+++ b/engine/src/test/resources/hibernate/Image.hbm.xml
@@ -1,17 +1,17 @@
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
-
-
- This class contains the image hashes and meta data
-
-
-
-
-
-
-
+
+
+ This class contains the image hashes and meta data
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/test/resources/hibernate/ImageHash.hbm.xml b/engine/src/test/resources/hibernate/ImageHash.hbm.xml
index 3d79acb..7cac328 100644
--- a/engine/src/test/resources/hibernate/ImageHash.hbm.xml
+++ b/engine/src/test/resources/hibernate/ImageHash.hbm.xml
@@ -1,18 +1,18 @@
+ "-//Hibernate/Hibernate Mapping DTD//EN"
+ "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
-
-
- This class contains the image hashes
-
-
-
-
-
-
-
-
-
+
+
+ This class contains the image hashes
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/test/resources/logback-minimum-config.xml b/engine/src/test/resources/logback-minimum-config.xml
index ca251cc..eba6025 100644
--- a/engine/src/test/resources/logback-minimum-config.xml
+++ b/engine/src/test/resources/logback-minimum-config.xml
@@ -1,59 +1,59 @@
-
- ImageTools.debug
-
- false
- [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- DEBUG
-
-
- 1
- ImageTools.debug.%i
-
-
- 5MB
-
-
-
- ImageTools.info
-
- false
- [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- INFO
-
-
- 1
- ImageTools.info.%i
-
-
- 500KB
-
-
-
- ImageTools.err
-
- false
- [%.16thread] [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- ERROR
-
-
- 1
- ImageTools.err.%i
-
-
- 500KB
-
-
-
-
-
-
-
+
+ ImageTools.debug
+
+ false
+ [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ DEBUG
+
+
+ 1
+ ImageTools.debug.%i
+
+
+ 5MB
+
+
+
+ ImageTools.info
+
+ false
+ [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ INFO
+
+
+ 1
+ ImageTools.info.%i
+
+
+ 500KB
+
+
+
+ ImageTools.err
+
+ false
+ [%.16thread] [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ ERROR
+
+
+ 1
+ ImageTools.err.%i
+
+
+ 500KB
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/engine/src/test/scala/com/sothr/imagetools/engine/BaseTest.scala b/engine/src/test/scala/com/sothr/imagetools/engine/BaseTest.scala
index aabfaff..d75b523 100644
--- a/engine/src/test/scala/com/sothr/imagetools/engine/BaseTest.scala
+++ b/engine/src/test/scala/com/sothr/imagetools/engine/BaseTest.scala
@@ -6,8 +6,8 @@ import org.scalatest.{BeforeAndAfter, FunSuite, Inside, Inspectors, Matchers, Op
abstract class BaseTest extends FunSuite with Matchers with OptionValues with Inside with Inspectors with BeforeAndAfter with Logging with Timing {
- before {
- AppConfig.configureApp()
- }
+ before {
+ AppConfig.configureApp()
+ }
-}
+}
\ No newline at end of file
diff --git a/engine/src/test/scala/com/sothr/imagetools/engine/EngineTest.scala b/engine/src/test/scala/com/sothr/imagetools/engine/EngineTest.scala
index 6e9cbad..6c2d8c3 100644
--- a/engine/src/test/scala/com/sothr/imagetools/engine/EngineTest.scala
+++ b/engine/src/test/scala/com/sothr/imagetools/engine/EngineTest.scala
@@ -1,44 +1,44 @@
package com.sothr.imagetools.engine
/**
- * Basic Test of the engines
- *
- * Created by drew on 1/26/14.
- */
+ * Basic Test of the engines
+ *
+ * Created by drew on 1/26/14.
+ */
class EngineTest extends BaseTest {
- test("SequentialEngine Test getImagesForDirectory for sample directory") {
- val engine: Engine = new SequentialEngine()
- assertResult(3) {
- engine.getImagesForDirectory("sample").length
- }
- }
+ test("SequentialEngine Test getImagesForDirectory for sample directory") {
+ val engine: Engine = new SequentialEngine()
+ assertResult(3) {
+ engine.getImagesForDirectory("sample").length
+ }
+ }
- test("SequentialEngine Test getSimilarImagesForDirectory for sample directory") {
- val engine = new SequentialEngine()
- val similarImages = engine.getSimilarImagesForDirectory("sample")
- assertResult(1) {
- similarImages.length
- }
- assertResult(3) {
- similarImages(0).similarImages.size
- }
- }
+ test("SequentialEngine Test getSimilarImagesForDirectory for sample directory") {
+ val engine = new SequentialEngine()
+ val similarImages = engine.getSimilarImagesForDirectory("sample")
+ assertResult(1) {
+ similarImages.length
+ }
+ assertResult(3) {
+ similarImages(0).similarImages.size
+ }
+ }
- test("ConcurrentEngine Test getImagesForDirectory for sample directory") {
- val engine: Engine = new ConcurrentEngine()
- assertResult(3) {
- engine.getImagesForDirectory("sample").length
- }
- }
+ test("ConcurrentEngine Test getImagesForDirectory for sample directory") {
+ val engine: Engine = new ConcurrentEngine()
+ assertResult(3) {
+ engine.getImagesForDirectory("sample").length
+ }
+ }
- test("ConcurrentEngine Test getSimilarImagesForDirectory for sample directory") {
- val engine = new ConcurrentEngine()
- val similarImages = engine.getSimilarImagesForDirectory("sample")
- assertResult(1) {
- similarImages.length
- }
- assertResult(3) {
- similarImages(0).similarImages.size
- }
- }
-}
+ test("ConcurrentEngine Test getSimilarImagesForDirectory for sample directory") {
+ val engine = new ConcurrentEngine()
+ val similarImages = engine.getSimilarImagesForDirectory("sample")
+ assertResult(1) {
+ similarImages.length
+ }
+ assertResult(3) {
+ similarImages(0).similarImages.size
+ }
+ }
+}
\ No newline at end of file
diff --git a/engine/src/test/scala/com/sothr/imagetools/engine/ScalaAppTest.scala b/engine/src/test/scala/com/sothr/imagetools/engine/ScalaAppTest.scala
index 404417e..145c669 100644
--- a/engine/src/test/scala/com/sothr/imagetools/engine/ScalaAppTest.scala
+++ b/engine/src/test/scala/com/sothr/imagetools/engine/ScalaAppTest.scala
@@ -2,8 +2,8 @@ package com.sothr.imagetools.engine
class ScalaAppTest extends BaseTest {
- test("I Do Nothing Just Make Sure The Framework Works") {
- assert(true)
- }
+ test("I Do Nothing Just Make Sure The Framework Works") {
+ assert(true)
+ }
-}
+}
\ No newline at end of file
diff --git a/engine/src/test/scala/com/sothr/imagetools/engine/TestParams.scala b/engine/src/test/scala/com/sothr/imagetools/engine/TestParams.scala
index b82d2eb..c0bfa74 100644
--- a/engine/src/test/scala/com/sothr/imagetools/engine/TestParams.scala
+++ b/engine/src/test/scala/com/sothr/imagetools/engine/TestParams.scala
@@ -1,7 +1,7 @@
package com.sothr.imagetools.engine
object TestParams {
- val LargeSampleImage1 = "sample/sample_01_large.jpg"
- val MediumSampleImage1 = "sample/sample_01_medium.jpg"
- val SmallSampleImage1 = "sample/sample_01_small.jpg"
+ val LargeSampleImage1 = "sample/sample_01_large.jpg"
+ val MediumSampleImage1 = "sample/sample_01_medium.jpg"
+ val SmallSampleImage1 = "sample/sample_01_small.jpg"
}
\ No newline at end of file
diff --git a/engine/src/test/scala/com/sothr/imagetools/engine/image/ImageFilterTest.scala b/engine/src/test/scala/com/sothr/imagetools/engine/image/ImageFilterTest.scala
index 8966525..be8995b 100644
--- a/engine/src/test/scala/com/sothr/imagetools/engine/image/ImageFilterTest.scala
+++ b/engine/src/test/scala/com/sothr/imagetools/engine/image/ImageFilterTest.scala
@@ -5,38 +5,38 @@ import java.io.File
import com.sothr.imagetools.engine.BaseTest
/**
- * Test to make sure that the image filters work
- *
- * Created by drew on 1/26/14.
- */
+ * Test to make sure that the image filters work
+ *
+ * Created by drew on 1/26/14.
+ */
class ImageFilterTest extends BaseTest {
- test("Confirm ImageFilter Works") {
- val filter: ImageFilter = new ImageFilter()
- val bogusDirectory = new File(".")
- assert(filter.accept(bogusDirectory, "test.png"))
- assert(filter.accept(bogusDirectory, "test.bmp"))
- assert(filter.accept(bogusDirectory, "test.gif"))
- assert(filter.accept(bogusDirectory, "test.jpg"))
- assert(filter.accept(bogusDirectory, "test.jpeg"))
- assert(filter.accept(bogusDirectory, "test.jpeg.jpg"))
- }
+ test("Confirm ImageFilter Works") {
+ val filter: ImageFilter = new ImageFilter()
+ val bogusDirectory = new File(".")
+ assert(filter.accept(bogusDirectory, "test.png"))
+ assert(filter.accept(bogusDirectory, "test.bmp"))
+ assert(filter.accept(bogusDirectory, "test.gif"))
+ assert(filter.accept(bogusDirectory, "test.jpg"))
+ assert(filter.accept(bogusDirectory, "test.jpeg"))
+ assert(filter.accept(bogusDirectory, "test.jpeg.jpg"))
+ }
- test("Confirm ImageFiler Fails") {
- val filter: ImageFilter = new ImageFilter()
- val bogusDirectory = new File(".")
- assertResult(false) {
- filter.accept(bogusDirectory, "test")
- }
- assertResult(false) {
- filter.accept(bogusDirectory, "test.mp4")
- }
- assertResult(false) {
- filter.accept(bogusDirectory, "test.gif.mp4")
- }
- assertResult(false) {
- filter.accept(bogusDirectory, "")
- }
- }
+ test("Confirm ImageFiler Fails") {
+ val filter: ImageFilter = new ImageFilter()
+ val bogusDirectory = new File(".")
+ assertResult(false) {
+ filter.accept(bogusDirectory, "test")
+ }
+ assertResult(false) {
+ filter.accept(bogusDirectory, "test.mp4")
+ }
+ assertResult(false) {
+ filter.accept(bogusDirectory, "test.gif.mp4")
+ }
+ assertResult(false) {
+ filter.accept(bogusDirectory, "")
+ }
+ }
-}
+}
\ No newline at end of file
diff --git a/gui/pom.xml b/gui/pom.xml
index 3d6cbf8..7de0573 100644
--- a/gui/pom.xml
+++ b/gui/pom.xml
@@ -1,126 +1,126 @@
- 4.0.0
+ 4.0.0
-
- com.sothr.imagetools
- parent
- 1.0.1
- ../parent
-
+
+ com.sothr.imagetools
+ parent
+ 1.0.1
+ ../parent
+
- gui
- 0.1.1
- jar
+ gui
+ 0.1.1
+ jar
- ImageTools-GUI
- The Graphical User Interface for Image-Tools
- http://imagetools.sothr.com
-
- Sothr Software
-
+ ImageTools-GUI
+ The Graphical User Interface for Image-Tools
+ http://imagetools.sothr.com
+
+ Sothr Software
+
-
-
- com.sothr.imagetools
- engine
-
-
- ch.qos.logback
- logback-core
-
-
- ch.qos.logback
- logback-classic
-
-
- ch.qos.logback
- logback-access
-
-
- org.slf4j
- slf4j-api
-
-
- org.clapper
- grizzled-slf4j_${scala.binary.version}
-
-
- org.scala-lang
- scala-library
-
-
- org.commonjava.googlecode.markdown4j
- markdown4j
-
-
+
+
+ com.sothr.imagetools
+ engine
+
+
+ ch.qos.logback
+ logback-core
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ ch.qos.logback
+ logback-access
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.clapper
+ grizzled-slf4j_${scala.binary.version}
+
+
+ org.scala-lang
+ scala-library
+
+
+ org.commonjava.googlecode.markdown4j
+ markdown4j
+
+
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- 2.4
-
-
- package
-
- jar
-
-
-
-
- true
- lib/
- com.sothr.imagetools.ui.App
-
-
-
- ${project.build.directory}/release
-
-
-
-
-
-
- maven-antrun-plugin
- 1.4
-
-
- prepare
- process-resources
-
-
-
-
-
-
-
-
-
- run
-
-
-
- package
- package
-
-
-
-
-
-
-
- run
-
-
-
-
-
-
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.4
+
+
+ package
+
+ jar
+
+
+
+
+ true
+ lib/
+ com.sothr.imagetools.ui.App
+
+
+
+ ${project.build.directory}/release
+
+
+
+
+
+
+ maven-antrun-plugin
+ 1.4
+
+
+ prepare
+ process-resources
+
+
+
+
+
+
+
+
+
+ run
+
+
+
+ package
+ package
+
+
+
+
+
+
+
+ run
+
+
+
+
+
+
diff --git a/gui/src/includes/logback.xml b/gui/src/includes/logback.xml
index 077b4d1..845280a 100644
--- a/gui/src/includes/logback.xml
+++ b/gui/src/includes/logback.xml
@@ -1,75 +1,75 @@
-
-
-
-
-
- false
- [%date{HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- INFO
-
-
-
-
- ImageTools.debug
-
- false
- [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- DEBUG
-
-
- 1
- ImageTools.debug.%i
-
-
- 5MB
-
-
-
-
- ImageTools.info
-
- false
- [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- INFO
-
-
- 1
- ImageTools.info.%i
-
-
- 500KB
-
-
-
-
- ImageTools.err
-
- false
- [%.16thread] [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
-
-
- ERROR
-
-
- 1
- ImageTools.err.%i
-
-
- 500KB
-
-
-
-
-
-
-
-
+
+
+
+
+
+ false
+ [%date{HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ INFO
+
+
+
+
+ ImageTools.debug
+
+ false
+ [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ DEBUG
+
+
+ 1
+ ImageTools.debug.%i
+
+
+ 5MB
+
+
+
+
+ ImageTools.info
+
+ false
+ [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ INFO
+
+
+ 1
+ ImageTools.info.%i
+
+
+ 500KB
+
+
+
+
+ ImageTools.err
+
+ false
+ [%.16thread] [%date{yy-MM-dd HH:mm:ss}] %-5level [%c{16}] - %message%n
+
+
+ ERROR
+
+
+ 1
+ ImageTools.err.%i
+
+
+ 500KB
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gui/src/main/java/com/sothr/imagetools/ui/App.java b/gui/src/main/java/com/sothr/imagetools/ui/App.java
index 0f7bb32..4dbfc97 100644
--- a/gui/src/main/java/com/sothr/imagetools/ui/App.java
+++ b/gui/src/main/java/com/sothr/imagetools/ui/App.java
@@ -24,78 +24,78 @@ import java.util.List;
*/
public class App extends Application {
- private static final String MAINGUI_FXML = "fxml/mainapp/MainApp.fxml";
- private static Logger logger;
+ private static final String MAINGUI_FXML = "fxml/mainapp/MainApp.fxml";
+ private static Logger logger;
- public static void main(String[] args) {
- AppConfig.configureApp();
+ public static void main(String[] args) {
+ AppConfig.configureApp();
- try {
- //try to run the UI
- launch(args);
- } catch (Exception ex) {
- logger.error("A fatal error has occurred: ", ex);
- //show popup about the error to the user then exit
- }
- }
+ try {
+ //try to run the UI
+ launch(args);
+ } catch (Exception ex) {
+ logger.error("A fatal error has occurred: ", ex);
+ //show popup about the error to the user then exit
+ }
+ }
- @Override
- public void init() throws Exception {
- AppConfig.configureApp();
- logger = LoggerFactory.getLogger(this.getClass());
- logger.info("Initializing Image Tools");
- List parameters = this.getParameters().getRaw();
- logger.info(String.format("Application was called with '%s' parameters", parameters.toString()));
- super.init();
- }
+ @Override
+ public void init() throws Exception {
+ AppConfig.configureApp();
+ logger = LoggerFactory.getLogger(this.getClass());
+ logger.info("Initializing Image Tools");
+ List parameters = this.getParameters().getRaw();
+ logger.info(String.format("Application was called with '%s' parameters", parameters.toString()));
+ super.init();
+ }
- @Override
- public void start(Stage primaryStage) throws Exception {
- logger.info("Image-Tools is starting");
- logger.info(String.format("Launching GUI with FXML file %s", MAINGUI_FXML));
- //store the primary stage globally for reference in popups and the like
- AppConfig.setPrimaryStage(primaryStage);
- try {
- //Confirm we have the plugin for JPEG color fixes
- Iterator readers = ImageIO.getImageReadersByFormatName("JPEG");
- while (readers.hasNext()) {
- logger.info("Image Reader Plugin: [{}] Available", new Object[]{readers.next()});
- }
+ @Override
+ public void start(Stage primaryStage) throws Exception {
+ logger.info("Image-Tools is starting");
+ logger.info(String.format("Launching GUI with FXML file %s", MAINGUI_FXML));
+ //store the primary stage globally for reference in popups and the like
+ AppConfig.setPrimaryStage(primaryStage);
+ try {
+ //Confirm we have the plugin for JPEG color fixes
+ Iterator readers = ImageIO.getImageReadersByFormatName("JPEG");
+ while (readers.hasNext()) {
+ logger.info("Image Reader Plugin: [{}] Available", new Object[]{readers.next()});
+ }
- FXMLLoader loader = new FXMLLoader();
- URL location = ResourceLoader.get().getResource(MAINGUI_FXML);
- loader.setLocation(location);
- loader.setBuilderFactory(new JavaFXBuilderFactory());
- Parent root = loader.load(location.openStream());
- //save the primary controller
- AppConfig.setFxmlLoader(loader);
- primaryStage.setScene(new Scene(root));
- //config main scene
- primaryStage.setTitle("Image Tools");
- primaryStage.setMinHeight(600.0);
- primaryStage.setMinWidth(800.0);
- primaryStage.setResizable(true);
- //show main scene
- primaryStage.show();
- } catch (IOException ioe) {
- String message = String.format("Unable to load FXML file: %s", MAINGUI_FXML);
- ImageToolsException ite = new ImageToolsException(message, ioe);
- logger.error(message, ioe);
- throw ite;
- } catch (Exception ex) {
- String message = "An unhandled exception was thrown by the GUI";
- ImageToolsException ite = new ImageToolsException(message, ex);
- logger.error(message, ex);
- throw ite;
- }
- }
+ FXMLLoader loader = new FXMLLoader();
+ URL location = ResourceLoader.get().getResource(MAINGUI_FXML);
+ loader.setLocation(location);
+ loader.setBuilderFactory(new JavaFXBuilderFactory());
+ Parent root = loader.load(location.openStream());
+ //save the primary controller
+ AppConfig.setFxmlLoader(loader);
+ primaryStage.setScene(new Scene(root));
+ //config main scene
+ primaryStage.setTitle("Image Tools");
+ primaryStage.setMinHeight(600.0);
+ primaryStage.setMinWidth(800.0);
+ primaryStage.setResizable(true);
+ //show main scene
+ primaryStage.show();
+ } catch (IOException ioe) {
+ String message = String.format("Unable to load FXML file: %s", MAINGUI_FXML);
+ ImageToolsException ite = new ImageToolsException(message, ioe);
+ logger.error(message, ioe);
+ throw ite;
+ } catch (Exception ex) {
+ String message = "An unhandled exception was thrown by the GUI";
+ ImageToolsException ite = new ImageToolsException(message, ex);
+ logger.error(message, ex);
+ throw ite;
+ }
+ }
- @Override
- public void stop() throws Exception {
- logger.info("Image-Tools is shutting down");
- AppConfig.shutdown();
- super.stop();
- //force the JVM to close
- System.exit(0);
- }
-}
+ @Override
+ public void stop() throws Exception {
+ logger.info("Image-Tools is shutting down");
+ AppConfig.shutdown();
+ super.stop();
+ //force the JVM to close
+ System.exit(0);
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/resources/fxml/mainapp/MainApp.fxml b/gui/src/main/resources/fxml/mainapp/MainApp.fxml
index 4e6afb4..ca6099b 100644
--- a/gui/src/main/resources/fxml/mainapp/MainApp.fxml
+++ b/gui/src/main/resources/fxml/mainapp/MainApp.fxml
@@ -2,196 +2,215 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala
index b47873a..aee626c 100644
--- a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala
+++ b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTile.scala
@@ -13,109 +13,109 @@ import grizzled.slf4j.Logging
import resource._
/**
- * ImageTile class that is a special VBox
- *
- * Created by drew on 8/22/14.
- */
+ * ImageTile class that is a special VBox
+ *
+ * Created by drew on 8/22/14.
+ */
class ImageTile(thumbnailWidth: Integer,
image: com.sothr.imagetools.engine.image.Image,
imageTilePane: ImageTilePane) extends VBox with Logging {
- val thisTile = this
- val imageData = image
- val preferedTileWidth = (thumbnailWidth + 8).toDouble
- val preferedTileHeight = (thumbnailWidth + 32).toDouble
- //set tile size
- this.setPrefSize(preferedTileWidth, preferedTileHeight)
- this.setMinSize(preferedTileWidth, preferedTileHeight)
- this.setMaxSize(preferedTileWidth, preferedTileHeight)
+ val thisTile = this
+ val imageData = image
+ val preferedTileWidth = (thumbnailWidth + 8).toDouble
+ val preferedTileHeight = (thumbnailWidth + 32).toDouble
+ //set tile size
+ this.setPrefSize(preferedTileWidth, preferedTileHeight)
+ this.setMinSize(preferedTileWidth, preferedTileHeight)
+ this.setMaxSize(preferedTileWidth, preferedTileHeight)
- //set padding on the tiles
- //this.setPadding(new Insets(10.0d,0.0d,10.0d,0.0d))
+ //set padding on the tiles
+ //this.setPadding(new Insets(10.0d,0.0d,10.0d,0.0d))
- this.setAlignment(Pos.CENTER)
- this.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler[MouseEvent] {
- override def handle(event: MouseEvent): Unit = {
- if (event.isShiftDown) {
- //multiple selection
- imageTilePane.addImageSelected(thisTile)
- //remove individual images with control
- } else if (event.isControlDown) {
- imageTilePane.removeImageSelected(thisTile)
- }
- else {
- if (event.isPrimaryButtonDown) {
- imageTilePane.imageSelected(thisTile)
- //double click
- if (event.getClickCount == 2) {
- // Look into http://stackoverflow.com/questions/228477/how-do-i-programmatically-determine-operating-system-in-java
- // for proper multi-platform opening support
- FileUtil.openInEditor(new File(image.getImagePath))
- } else {
+ this.setAlignment(Pos.CENTER)
+ this.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler[MouseEvent] {
+ override def handle(event: MouseEvent): Unit = {
+ if (event.isShiftDown) {
+ //multiple selection
+ imageTilePane.addImageSelected(thisTile)
+ //remove individual images with control
+ } else if (event.isControlDown) {
+ imageTilePane.removeImageSelected(thisTile)
+ }
+ else {
+ if (event.isPrimaryButtonDown) {
+ imageTilePane.imageSelected(thisTile)
+ //double click
+ if (event.getClickCount == 2) {
+ // Look into http://stackoverflow.com/questions/228477/how-do-i-programmatically-determine-operating-system-in-java
+ // for proper multi-platform opening support
+ FileUtil.openInEditor(new File(image.getImagePath))
+ } else {
- }
- } else if (event.isSecondaryButtonDown) {
- //right click context menu
- debug("Requesting Context Menu")
- imageTilePane.addImageSelected(thisTile)
- val contextMenuEvent = new ContextMenuEvent(
- thisTile,
- thisTile,
- ContextMenuEvent.CONTEXT_MENU_REQUESTED,
- event.getX, event.getY,
- event.getScreenX, event.getScreenY,
- false,
- new PickResult(thisTile, event.getSceneX, event.getSceneY))
- imageTilePane.handleContextMenu(contextMenuEvent)
- }
- }
- }
- })
+ }
+ } else if (event.isSecondaryButtonDown) {
+ //right click context menu
+ debug("Requesting Context Menu")
+ imageTilePane.addImageSelected(thisTile)
+ val contextMenuEvent = new ContextMenuEvent(
+ thisTile,
+ thisTile,
+ ContextMenuEvent.CONTEXT_MENU_REQUESTED,
+ event.getX, event.getY,
+ event.getScreenX, event.getScreenY,
+ false,
+ new PickResult(thisTile, event.getSceneX, event.getSceneY))
+ imageTilePane.handleContextMenu(contextMenuEvent)
+ }
+ }
+ }
+ })
- //Separator
- val separator = new Separator()
- separator.setOrientation(Orientation.HORIZONTAL)
- separator.setMaxHeight(5.0d)
- separator.setVisible(false)
- this.getChildren.add(separator)
+ //Separator
+ val separator = new Separator()
+ separator.setOrientation(Orientation.HORIZONTAL)
+ separator.setMaxHeight(5.0d)
+ separator.setVisible(false)
+ this.getChildren.add(separator)
- // Image
- val genImageView = new ImageView()
- debug(s"Getting thumbnail from: ${image.getThumbnailPath}")
- managed(new FileInputStream(image.getThumbnailPath)) acquireAndGet {
- thumbSource =>
- val thumbnail = new javafx.scene.image.Image(thumbSource)
- genImageView.setImage(thumbnail)
- if (thumbnail.getHeight > thumbnail.getWidth) {
- genImageView.setFitHeight(128.0)
- } else {
- genImageView.setFitWidth(128.0)
- }
- }
- genImageView.setPreserveRatio(true)
+ // Image
+ val genImageView = new ImageView()
+ debug(s"Getting thumbnail from: ${image.getThumbnailPath}")
+ managed(new FileInputStream(image.getThumbnailPath)) acquireAndGet {
+ thumbSource =>
+ val thumbnail = new javafx.scene.image.Image(thumbSource)
+ genImageView.setImage(thumbnail)
+ if (thumbnail.getHeight > thumbnail.getWidth) {
+ genImageView.setFitHeight(128.0)
+ } else {
+ genImageView.setFitWidth(128.0)
+ }
+ }
+ genImageView.setPreserveRatio(true)
- this.getChildren.add(genImageView)
+ this.getChildren.add(genImageView)
- //Label
- val imageLabel = new Label()
- imageLabel.setText(s"${image.getHeight}x${image.getWidth}")
- imageLabel.setWrapText(true)
- imageLabel.setMaxHeight(32d)
- imageLabel.setMaxWidth(preferedTileWidth - 2)
- imageLabel.setAlignment(Pos.BOTTOM_CENTER)
+ //Label
+ val imageLabel = new Label()
+ imageLabel.setText(s"${image.getHeight}x${image.getWidth}")
+ imageLabel.setWrapText(true)
+ imageLabel.setMaxHeight(32d)
+ imageLabel.setMaxWidth(preferedTileWidth - 2)
+ imageLabel.setAlignment(Pos.BOTTOM_CENTER)
- //Tooltip
- val tooltip = new Tooltip()
- tooltip.setText(s"${image.getName}")
- imageLabel.setTooltip(tooltip)
- this.getChildren.add(imageLabel)
+ //Tooltip
+ val tooltip = new Tooltip()
+ tooltip.setText(s"${image.getName}")
+ imageLabel.setTooltip(tooltip)
+ this.getChildren.add(imageLabel)
- //this.setOnContextMenuRequested(new EventHandler[ContextMenuEvent] {
- // override def handle(event: ContextMenuEvent): Unit = {
- // imageTilePane.handleContextMenu(event)
- // }
- //})
+ //this.setOnContextMenuRequested(new EventHandler[ContextMenuEvent] {
+ // override def handle(event: ContextMenuEvent): Unit = {
+ // imageTilePane.handleContextMenu(event)
+ // }
+ //})
- def getImageData: com.sothr.imagetools.engine.image.Image = {
- imageData
- }
-}
+ def getImageData: com.sothr.imagetools.engine.image.Image = {
+ imageData
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTileFactory.scala b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTileFactory.scala
index 7373e1d..b423280 100644
--- a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTileFactory.scala
+++ b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTileFactory.scala
@@ -7,19 +7,18 @@ import com.sothr.imagetools.engine.util.PropertiesService
import grizzled.slf4j.Logging
/**
- * Created by drew on 8/6/14.
- *
- * Creates pre-generated image tiles that can be rendered to a scene
- */
+ * Created by drew on 8/6/14.
+ *
+ * Creates pre-generated image tiles that can be rendered to a scene
+ */
object ImageTileFactory extends Logging {
- def get(image: com.sothr.imagetools.engine.image.Image, pane: TilePane): ImageTile = {
- val thumbnailWidth = PropertiesService.get("app.thumbnail.size", "128").toInt
- val imageTile = new ImageTile(thumbnailWidth, image, pane.asInstanceOf[ImageTilePane])
- //set padding
- imageTile.setPadding(new Insets(2, 2, 2, 2))
+ def get(image: com.sothr.imagetools.engine.image.Image, pane: TilePane): ImageTile = {
+ val thumbnailWidth = PropertiesService.get("app.thumbnail.size", "128").toInt
+ val imageTile = new ImageTile(thumbnailWidth, image, pane.asInstanceOf[ImageTilePane])
+ //set padding
+ imageTile.setPadding(new Insets(2, 2, 2, 2))
- imageTile
- }
-
-}
+ imageTile
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala
index 49f0004..f2d75f3 100644
--- a/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala
+++ b/gui/src/main/scala/com/sothr/imagetools/ui/component/ImageTilePane.scala
@@ -20,266 +20,265 @@ import scala.concurrent._
import scala.util.{Failure, Success}
/**
- * Custom Tile Pane with a multi selection model
- *
- * Created by drew on 8/29/14.
- */
+ * Custom Tile Pane with a multi selection model
+ *
+ * Created by drew on 8/29/14.
+ */
class ImageTilePane extends TilePane with Logging {
- val selectionModel = new ImageTilePaneSelectionModel[ImageTile](this)
-
- def handleContextMenu(event: ContextMenuEvent) = {
- //Build and show a context menu
- debug("Context Menu Request Received")
- val numSelected = this.selectionModel.getSelectedIndices.size()
- if (numSelected > 0) {
- if (numSelected == 1) {
- val contextMenu = getSingleSelectionContextMenu
- debug("Showing context menu")
- contextMenu.show(event.getTarget.asInstanceOf[Node], Side.RIGHT, 0d, 0d)
- } else {
- val contextMenu = getMulipleSelectionContextMenu
- debug("Showing context menu")
- contextMenu.show(event.getTarget.asInstanceOf[Node], Side.RIGHT, 0d, 0d)
- }
- }
- }
-
- def getSingleSelectionContextMenu: ContextMenu = {
- debug("Building single-selection context menu")
- val contextMenu = new ContextMenu()
- val delete = new MenuItem("Delete")
- delete.setOnAction(new EventHandler[ActionEvent]() {
- def handle(e: ActionEvent) = {
- debug("Requesting Single Delete")
- deleteSelected()
- }
- })
- contextMenu.getItems.addAll(delete)
- contextMenu
- }
-
- def getMulipleSelectionContextMenu: ContextMenu = {
- debug("Building multi-selection context menu")
- val contextMenu = new ContextMenu()
- val delete = new MenuItem("Delete")
- delete.setOnAction(new EventHandler[ActionEvent]() {
- def handle(e: ActionEvent) = {
- debug("Requesting Multiple Delete")
- deleteSelected()
- }
- })
- contextMenu.getItems.addAll(delete)
- contextMenu
- }
-
- def imageSelected(imageTile: ImageTile) = {
- this.selectionModel.clearAndSelect(this.getChildren.indexOf(imageTile))
- }
-
- def addImageSelected(imageTile: ImageTile) = {
- this.selectionModel.select(this.getChildren.indexOf(imageTile))
- }
-
- def removeImageSelected(imageTile: ImageTile) = {
- this.selectionModel.clearSelection(this.getChildren.indexOf(imageTile))
- }
-
- def clearSelection() = {
- this.selectionModel.clearSelection()
- }
-
- //request deletion of selected images
- def deleteSelected() = {
- val f: Future[Unit] = Future {
- val selected = this.selectionModel.getSelectedItems
- val iterator = selected.iterator()
- while (iterator.hasNext) {
- val item = iterator.next()
- val imageTile = item.asInstanceOf[ImageTile]
- val data = imageTile.imageData
- val controller = AppConfig.getFxmlLoader.getController[AppController]()
- controller.engine.deleteImage(data)
-
- }
- val pane = this
- Platform.runLater(new Runnable() {
- override def run(): Unit = {
- //remove from the current panel
- pane.getChildren.removeAll(selected)
- clearSelection()
- }
- })
- }
- f onComplete {
- case Success(a) => info("Successfully deleted files")
- case Failure(f) => error("Failed to delete files", f)
- }
- }
+ val selectionModel = new ImageTilePaneSelectionModel[ImageTile](this)
+
+ def handleContextMenu(event: ContextMenuEvent) = {
+ //Build and show a context menu
+ debug("Context Menu Request Received")
+ val numSelected = this.selectionModel.getSelectedIndices.size()
+ if (numSelected > 0) {
+ if (numSelected == 1) {
+ val contextMenu = getSingleSelectionContextMenu
+ debug("Showing context menu")
+ contextMenu.show(event.getTarget.asInstanceOf[Node], Side.RIGHT, 0d, 0d)
+ } else {
+ val contextMenu = getMulipleSelectionContextMenu
+ debug("Showing context menu")
+ contextMenu.show(event.getTarget.asInstanceOf[Node], Side.RIGHT, 0d, 0d)
+ }
+ }
+ }
+
+ def getSingleSelectionContextMenu: ContextMenu = {
+ debug("Building single-selection context menu")
+ val contextMenu = new ContextMenu()
+ val delete = new MenuItem("Delete")
+ delete.setOnAction(new EventHandler[ActionEvent]() {
+ def handle(e: ActionEvent) = {
+ debug("Requesting Single Delete")
+ deleteSelected()
+ }
+ })
+ contextMenu.getItems.addAll(delete)
+ contextMenu
+ }
+
+ def getMulipleSelectionContextMenu: ContextMenu = {
+ debug("Building multi-selection context menu")
+ val contextMenu = new ContextMenu()
+ val delete = new MenuItem("Delete")
+ delete.setOnAction(new EventHandler[ActionEvent]() {
+ def handle(e: ActionEvent) = {
+ debug("Requesting Multiple Delete")
+ deleteSelected()
+ }
+ })
+ contextMenu.getItems.addAll(delete)
+ contextMenu
+ }
+
+ def imageSelected(imageTile: ImageTile) = {
+ this.selectionModel.clearAndSelect(this.getChildren.indexOf(imageTile))
+ }
+
+ def addImageSelected(imageTile: ImageTile) = {
+ this.selectionModel.select(this.getChildren.indexOf(imageTile))
+ }
+
+ def removeImageSelected(imageTile: ImageTile) = {
+ this.selectionModel.clearSelection(this.getChildren.indexOf(imageTile))
+ }
+
+ def clearSelection() = {
+ this.selectionModel.clearSelection()
+ }
+
+ //request deletion of selected images
+ def deleteSelected() = {
+ val f: Future[Unit] = Future {
+ val selected = this.selectionModel.getSelectedItems
+ val iterator = selected.iterator()
+ while (iterator.hasNext) {
+ val item = iterator.next()
+ val imageTile = item.asInstanceOf[ImageTile]
+ val data = imageTile.imageData
+ val controller = AppConfig.getFxmlLoader.getController[AppController]()
+ controller.engine.deleteImage(data)
+
+ }
+ val pane = this
+ Platform.runLater(new Runnable() {
+ override def run(): Unit = {
+ //remove from the current panel
+ pane.getChildren.removeAll(selected)
+ clearSelection()
+ }
+ })
+ }
+ f onComplete {
+ case Success(a) => info("Successfully deleted files")
+ case Failure(f) => error("Failed to delete files", f)
+ }
+ }
}
/**
- * Multiple selection model for ImageTilePane
- *
- */
+ * Multiple selection model for ImageTilePane
+ *
+ */
class ImageTilePaneSelectionModel[ImageTile](parentTilePane: ImageTilePane) extends MultipleSelectionModel[ImageTile] {
- val selectedIndexes: ObservableList[Integer] = new ArrayObservableList[Integer]()
-
- override def getSelectedIndices: ObservableList[Integer] = {
- this.selectedIndexes
- }
-
- override def getSelectedItems: ObservableList[ImageTile] = {
- val selected = new ArrayObservableList[ImageTile]()
- val iterator = selectedIndexes.iterator()
- while (iterator.hasNext) {
- selected.add(this.parentTilePane.getChildren.get(iterator.next()).asInstanceOf[ImageTile])
- }
- selected
- }
-
- override def selectIndices(index: Int, indices: Int*): Unit = {
- clearSelectionFormatting
- this.selectedIndexes.clear()
- setSelectionFormatting(index)
- this.selectedIndexes.add(index)
- for (i <- indices) {
- setSelectionFormatting(i)
- this.selectedIndexes.add(i)
- }
- }
-
- override def selectAll(): Unit = {
- clearSelectionFormatting
- this.selectedIndexes.clear()
- for (index <- 0 until this.parentTilePane.getChildren.size()) {
- setSelectionFormatting(index)
- this.selectedIndexes.add(index)
- }
- }
-
- override def selectFirst(): Unit = {
- clearSelectionFormatting
- this.selectedIndexes.clear()
- setSelectionFormatting(0)
- this.selectedIndexes.add(0)
- }
-
- override def selectLast(): Unit = {
- clearSelectionFormatting
- this.selectedIndexes.clear()
- setSelectionFormatting(this.parentTilePane.getChildren.size() - 1)
- this.selectedIndexes.add(this.parentTilePane.getChildren.size() - 1)
- }
-
- override def clearAndSelect(index: Int): Unit = {
- clearSelectionFormatting
- this.selectedIndexes.clear()
- setSelectionFormatting(index)
- this.selectedIndexes.add(index)
- }
-
- override def clearSelection(index: Int): Unit = {
- val i = this.selectedIndexes.indexOf(index)
- if (i >= 0) {
- clearSelectionFormatting(index)
- this.selectedIndexes.remove(i)
- }
- }
-
- private def clearSelectionFormatting(index: Int) = {
- val tile = this.parentTilePane.getChildren.get(index).asInstanceOf[VBox]
- tile.setBorder(Border.EMPTY)
- }
-
- override def clearSelection(): Unit = {
- clearSelectionFormatting
- this.selectedIndexes.clear()
- }
-
- override def selectPrevious(): Unit = {
- if (this.selectedIndexes.size == 1) {
- val currentIndex = this.selectedIndexes.get(0)
- val nextIndex = if (currentIndex < 1) 0 else currentIndex - 1
- this.selectedIndexes.set(0, nextIndex)
- }
- }
-
- override def selectNext(): Unit = {
- if (this.selectedIndexes.size == 1) {
- val currentIndex = this.selectedIndexes.get(0)
- val nextIndex = if (currentIndex >= this.parentTilePane.getChildren.size - 1) this.parentTilePane.getChildren.size - 1 else currentIndex + 1
- this.selectedIndexes.set(0, nextIndex)
- }
- }
-
- override def select(index: Int): Unit = {
- //can only select once
- if (!this.selectedIndexes.contains(index)) {
- setSelectionFormatting(index)
- this.selectedIndexes.add(index)
- }
- }
-
- private def setSelectionFormatting(index: Int): Unit = {
- setSelectionFormatting(this.parentTilePane.getChildren.get(index).asInstanceOf[ImageTile])
- }
-
- private def setSelectionFormatting(imageTile: ImageTile) = {
- val borderStroke = new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN)
- imageTile.asInstanceOf[VBox].setBorder(new Border(borderStroke))
- }
-
- override def select(obj: ImageTile): Unit = {
- if (this.parentTilePane.getChildren.contains(obj)) {
- clearSelectionFormatting
- this.selectedIndexes.clear()
- setSelectionFormatting(obj)
- this.selectedIndexes.add(this.parentTilePane.getChildren.indexOf(obj))
- }
- }
-
- private def clearSelectionFormatting() = {
- val iterator = this.parentTilePane.getChildren.iterator()
- while (iterator.hasNext) {
- //remove the selection styling
- val imageTile: VBox = iterator.next().asInstanceOf[VBox]
- imageTile.setBorder(Border.EMPTY)
- }
- }
-
- override def isEmpty: Boolean = {
- this.parentTilePane.getChildren.isEmpty
- }
-
- override def isSelected(index: Int): Boolean = {
- this.selectedIndexes.contains(index)
- }
+ val selectedIndexes: ObservableList[Integer] = new ArrayObservableList[Integer]()
+
+ override def getSelectedIndices: ObservableList[Integer] = {
+ this.selectedIndexes
+ }
+
+ override def getSelectedItems: ObservableList[ImageTile] = {
+ val selected = new ArrayObservableList[ImageTile]()
+ val iterator = selectedIndexes.iterator()
+ while (iterator.hasNext) {
+ selected.add(this.parentTilePane.getChildren.get(iterator.next()).asInstanceOf[ImageTile])
+ }
+ selected
+ }
+
+ override def selectIndices(index: Int, indices: Int*): Unit = {
+ clearSelectionFormatting
+ this.selectedIndexes.clear()
+ setSelectionFormatting(index)
+ this.selectedIndexes.add(index)
+ for (i <- indices) {
+ setSelectionFormatting(i)
+ this.selectedIndexes.add(i)
+ }
+ }
+
+ override def selectAll(): Unit = {
+ clearSelectionFormatting
+ this.selectedIndexes.clear()
+ for (index <- 0 until this.parentTilePane.getChildren.size()) {
+ setSelectionFormatting(index)
+ this.selectedIndexes.add(index)
+ }
+ }
+
+ override def selectFirst(): Unit = {
+ clearSelectionFormatting
+ this.selectedIndexes.clear()
+ setSelectionFormatting(0)
+ this.selectedIndexes.add(0)
+ }
+
+ override def selectLast(): Unit = {
+ clearSelectionFormatting
+ this.selectedIndexes.clear()
+ setSelectionFormatting(this.parentTilePane.getChildren.size() - 1)
+ this.selectedIndexes.add(this.parentTilePane.getChildren.size() - 1)
+ }
+
+ override def clearAndSelect(index: Int): Unit = {
+ clearSelectionFormatting
+ this.selectedIndexes.clear()
+ setSelectionFormatting(index)
+ this.selectedIndexes.add(index)
+ }
+
+ private def setSelectionFormatting(index: Int): Unit = {
+ setSelectionFormatting(this.parentTilePane.getChildren.get(index).asInstanceOf[ImageTile])
+ }
+
+ private def setSelectionFormatting(imageTile: ImageTile) = {
+ val borderStroke = new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderStroke.THIN)
+ imageTile.asInstanceOf[VBox].setBorder(new Border(borderStroke))
+ }
+
+ private def clearSelectionFormatting() = {
+ val iterator = this.parentTilePane.getChildren.iterator()
+ while (iterator.hasNext) {
+ //remove the selection styling
+ val imageTile: VBox = iterator.next().asInstanceOf[VBox]
+ imageTile.setBorder(Border.EMPTY)
+ }
+ }
+
+ override def clearSelection(index: Int): Unit = {
+ val i = this.selectedIndexes.indexOf(index)
+ if (i >= 0) {
+ clearSelectionFormatting(index)
+ this.selectedIndexes.remove(i)
+ }
+ }
+
+ private def clearSelectionFormatting(index: Int) = {
+ val tile = this.parentTilePane.getChildren.get(index).asInstanceOf[VBox]
+ tile.setBorder(Border.EMPTY)
+ }
+
+ override def clearSelection(): Unit = {
+ clearSelectionFormatting
+ this.selectedIndexes.clear()
+ }
+
+ override def selectPrevious(): Unit = {
+ if (this.selectedIndexes.size == 1) {
+ val currentIndex = this.selectedIndexes.get(0)
+ val nextIndex = if (currentIndex < 1) 0 else currentIndex - 1
+ this.selectedIndexes.set(0, nextIndex)
+ }
+ }
+
+ override def selectNext(): Unit = {
+ if (this.selectedIndexes.size == 1) {
+ val currentIndex = this.selectedIndexes.get(0)
+ val nextIndex = if (currentIndex >= this.parentTilePane.getChildren.size - 1) this.parentTilePane.getChildren.size - 1 else currentIndex + 1
+ this.selectedIndexes.set(0, nextIndex)
+ }
+ }
+
+ override def select(index: Int): Unit = {
+ //can only select once
+ if (!this.selectedIndexes.contains(index)) {
+ setSelectionFormatting(index)
+ this.selectedIndexes.add(index)
+ }
+ }
+
+ override def select(obj: ImageTile): Unit = {
+ if (this.parentTilePane.getChildren.contains(obj)) {
+ clearSelectionFormatting
+ this.selectedIndexes.clear()
+ setSelectionFormatting(obj)
+ this.selectedIndexes.add(this.parentTilePane.getChildren.indexOf(obj))
+ }
+ }
+
+ override def isEmpty: Boolean = {
+ this.parentTilePane.getChildren.isEmpty
+ }
+
+ override def isSelected(index: Int): Boolean = {
+ this.selectedIndexes.contains(index)
+ }
}
class ArrayObservableList[E] extends ModifiableObservableListBase[E] {
- val delegate: util.ArrayList[E] = new util.ArrayList[E]()
+ val delegate: util.ArrayList[E] = new util.ArrayList[E]()
- def get(index: Int): E = {
- delegate.get(index)
- }
+ def get(index: Int): E = {
+ delegate.get(index)
+ }
- def size = {
- delegate.size
- }
+ def size = {
+ delegate.size
+ }
- def doAdd(index: Int, element: E) = {
- delegate.add(index, element)
- }
+ def doAdd(index: Int, element: E) = {
+ delegate.add(index, element)
+ }
- def doSet(index: Int, element: E): E = {
- delegate.set(index, element)
- }
-
- def doRemove(index: Int): E = {
- delegate.remove(index)
- }
+ def doSet(index: Int, element: E): E = {
+ delegate.set(index, element)
+ }
+ def doRemove(index: Int): E = {
+ delegate.remove(index)
+ }
}
\ No newline at end of file
diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala b/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala
index 59d03f9..4f5fddb 100644
--- a/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala
+++ b/gui/src/main/scala/com/sothr/imagetools/ui/controller/AppController.scala
@@ -27,435 +27,434 @@ import scala.concurrent._
import scala.util.{Failure, Success}
/**
- * Main Application controller
- *
- * Created by drew on 12/31/13.
- */
+ * Main Application controller
+ *
+ * Created by drew on 12/31/13.
+ */
class AppController extends Logging {
- // Engine
- val engine: Engine = new ConcurrentEngine()
- //Define controls
- @FXML var rootPane: AnchorPane = null
- @FXML var rootMenuBar: MenuBar = null
- @FXML var scrollPane: ScrollPane = null
- @FXML var imageTilePane: TilePane = null
- @FXML var tagListView: ListView[String] = null
- // Labels
- @FXML var selectedDirectoryLabel: Label = null
- @FXML var currentDirectoryLabel: Label = null
- @FXML var progressLabel: Label = null
- // Others
- @FXML var progressBar: ProgressBar = null
- @FXML var paginator: Pagination = null
- @FXML var doRecursiveProcessing: CheckBox = null
- // Current State
- var currentDirectory: String = "."
- var currentImages: List[Image] = List[Image]()
-
- @FXML def initialize() = {
- if (PropertiesService.has("app.ui.lastPath")) {
- currentDirectory = PropertiesService.get("app.ui.lastPath", ".")
- selectedDirectoryLabel.setText(PropertiesService.get("app.ui.lastPath", ""))
- }
-
- //setup the engine listener
- val system: ActorSystem = AppConfig.getAppActorSystem
- val guiListenerProps: Props = Props.create(classOf[GUIEngineListener])
- val guiListener: ActorRef = system.actorOf(guiListenerProps)
- // configure the listener
- guiListener ! SetupListener(progressBar, progressLabel)
- // tell the engine to use our listener
- this.engine.setSearchedListener(guiListener)
- this.engine.setProcessedListener(guiListener)
- this.engine.setSimilarityListener(guiListener)
- // Initialize the progress label
- guiListener ! SubmitMessage("Initialized System... Ready!")
-
- // set the default images per page if it doesn't exist yet
- if (!PropertiesService.has("app.ui.thumbsPerPage")) {
- PropertiesService.set("app.ui.thumbsPerPage", "100")
- }
-
- //setup the paginator
- //font size doesn't increase the size of the buttons
- //paginator.setStyle("-fx-font-size:13;")
- // configure the page factory
- paginator.setPageFactory(new Callback[Integer, Node]() {
- override def call(pageIndex: Integer): Node = {
- // do all of our display logic
- showPage(pageIndex)
- // override behavior to display anything
- new VBox()
- }
- })
-
- //override the imageTilePane
- debug("Replacing the default TilePane with a custom ImageTilePane")
- val newImageTilePane = new ImageTilePane()
- newImageTilePane.setHgap(this.imageTilePane.getHgap)
- newImageTilePane.setVgap(this.imageTilePane.getVgap)
- newImageTilePane.setMinHeight(this.imageTilePane.getMinHeight)
- newImageTilePane.setMinWidth(this.imageTilePane.getMinWidth)
- newImageTilePane.setMaxHeight(this.imageTilePane.getMaxHeight)
- newImageTilePane.setMaxWidth(this.imageTilePane.getMaxWidth)
- newImageTilePane.setPrefColumns(this.imageTilePane.getPrefColumns)
- newImageTilePane.setPrefRows(this.imageTilePane.getPrefRows)
- //newImageTilePane.setPrefTileHeight(this.imageTilePane.getPrefTileHeight)
- //newImageTilePane.setPrefTileWidth(this.imageTilePane.getPrefTileWidth)
- newImageTilePane.setTileAlignment(this.imageTilePane.getTileAlignment)
- debug("Assigning the the new ImageTilePane to the ScrollPane")
- this.scrollPane.setContent(newImageTilePane)
- this.imageTilePane = newImageTilePane
-
- //test
- //val testImage = new Image()
- //testImage.setThumbnailPath("test.jpg")
- //testImage.setImagePath("test.jpg")
- //val testImageList = new mutable.MutableList[Image]
- //for (i <- 1 to 100) {
- // testImageList += testImage
- //}
- //setPagesContent(testImageList.toList)
- //showPage(0)
- //val list = FXCollections.observableArrayList[String]()
- //for (i <- 1 to 100) {
- // list.add(s"test-item ${i}")
- //}
- //tagListView.setItems(list)
- }
-
- //region MenuItem Actions
-
- @FXML
- def helpAction(event: ActionEvent) = {
- showExternalHTMLUtilityDialog("http://www.sothr.com")
- }
-
- def showExternalHTMLUtilityDialog(url: String) = {
- val dialog: Stage = new Stage()
- dialog.initStyle(StageStyle.UTILITY)
- val parent: Group = new Group()
-
- //setup the HTML view
- val htmlView = new WebView
- htmlView.getEngine.load(url)
- //htmlView.setMinWidth(width)
- //htmlView.setMinHeight(height)
- //htmlView.setPrefWidth(width)
- //htmlView.setPrefHeight(height)
- parent.getChildren.add(htmlView)
-
- val scene: Scene = new Scene(parent)
- dialog.setScene(scene)
- dialog.setResizable(false)
- dialog.setTitle(htmlView.getEngine.getTitle)
- dialog.show()
- }
-
- @FXML
- def aboutAction(event: ActionEvent) = {
- debug("Displaying about screen")
- var aboutMessage = "Simple About Message"
- try {
-
- val scanner = new Scanner(ResourceLoader.get().getResourceStream("documents/about.md"))
- aboutMessage = ""
- while (scanner.hasNextLine) {
- aboutMessage += scanner.nextLine().trim() + "\n"
- }
-
- debug(s"Parsed About Message: '$aboutMessage'")
-
- } catch {
- case ioe: IOException =>
- error("Unable to read about file")
- }
-
- showMarkdownUtilityDialog("About", aboutMessage, 400.0, 300.0)
- debug("Showing About Dialog")
- }
-
- //endregion
-
- //region buttons
-
- //todo: show a dialog that is rendered from markdown content
- def showMarkdownUtilityDialog(title: String, markdown: String, width: Double = 800.0, height: Double = 600.0) = {
- val htmlBody = new Markdown4jProcessor().process(markdown)
- showHTMLUtilityDialog(title, htmlBody, width, height)
- }
-
- /**
- * Render HTML content to a utility dialog. No input or output, just raw rendered content through a webkit engine.
- *
- * @param title Title of the dialog
- * @param htmlBody Body to render
- * @param width Desired width of the dialog
- * @param height Desired height of the dialog
- */
- def showHTMLUtilityDialog(title: String, htmlBody: String, width: Double = 800.0, height: Double = 600.0) = {
- val dialog: Stage = new Stage()
- dialog.initStyle(StageStyle.UTILITY)
- val parent: Group = new Group()
-
- //setup the HTML view
- val htmlView = new WebView
- htmlView.getEngine.loadContent(htmlBody)
- htmlView.setMinWidth(width)
- htmlView.setMinHeight(height)
- htmlView.setPrefWidth(width)
- htmlView.setPrefHeight(height)
- parent.getChildren.add(htmlView)
-
- val scene: Scene = new Scene(parent)
- dialog.setScene(scene)
- dialog.setResizable(false)
- dialog.setTitle(title)
- dialog.show()
- }
-
- @FXML
- def closeAction(event: ActionEvent) = {
- debug("Closing application from the menu bar")
- val stage: Stage = this.rootMenuBar.getScene.getWindow.asInstanceOf[Stage]
- stage.close()
- }
-
- //endregion
-
- //region pagination
-
- @FXML
- def browseFolders(event: ActionEvent) = {
- val chooser = new DirectoryChooser()
- chooser.setTitle("ImageTools Browser")
-
- try {
- val defaultDirectory = new File(currentDirectory)
- chooser.setInitialDirectory(defaultDirectory)
- val window = this.rootPane.getScene.getWindow
- val selectedDirectory = chooser.showDialog(window)
- info(s"Selected Directory: ${selectedDirectory.getAbsolutePath}")
- selectedDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
-
- currentDirectory = selectedDirectory.getAbsolutePath
- PropertiesService.set("app.ui.lastPath", selectedDirectory.getAbsolutePath)
- this.currentDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
- } catch {
- // fall back on the default because the directory we tried probably didn't exist
- case iae: IllegalArgumentException =>
- logger.error("The old directory no longer exists", iae)
- chooser.setInitialDirectory(null)
- val window = this.rootPane.getScene.getWindow
- val selectedDirectory = chooser.showDialog(window)
- info(s"Selected Directory: ${selectedDirectory.getAbsolutePath}")
- selectedDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
-
- currentDirectory = selectedDirectory.getAbsolutePath
- PropertiesService.set("app.ui.lastPath", selectedDirectory.getAbsolutePath)
- this.currentDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
- }
- }
-
- @FXML
- def showAllImages(event: ActionEvent) = {
- resetPaginator()
- getImageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
- val f: Future[List[Image]] = Future {
- val images = engine.getImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
- images.sortWith((x, y) => x.imagePath < y.imagePath)
- }
-
- f onComplete {
- case Success(images) =>
- info(s"Displaying ${images.length} images")
- // This is used so that JavaFX updates on the proper thread
- // This is important since UI updates can only happen on that thread
- Platform.runLater(new Runnable() {
- override def run() {
- setPagesContent(images)
- showPage(0)
- }
- })
- case Failure(t) =>
- error("An Error Occurred", t)
- }
- }
-
- def resetPaginator() = {
- this.paginator.setDisable(true)
- this.paginator.setPageCount(1)
- }
-
- //endregion
-
- def setPagesContent(images: List[Image]) = {
- this.currentImages = images
- //set the appropriate size for the pagination
- val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt
- val pageNum = Math.ceil(this.currentImages.size.toFloat / itemsPerPage).toInt
- this.paginator.setPageCount(pageNum)
- this.paginator.setDisable(false)
- }
-
- //todo: include a templating engine for rendering information
-
- def showPage(pageIndex: Integer) = {
- val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt
- val startIndex = pageIndex * itemsPerPage
- val endIndex = if ((startIndex + itemsPerPage) > this.currentImages.size) this.currentImages.length else startIndex + itemsPerPage
- //clear any selections
- getImageTilePane.asInstanceOf[ImageTilePane].clearSelection()
- //clear and populate the scrollpane
- getImageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
- val images = this.currentImages.slice(startIndex, endIndex)
- Platform.runLater(new Runnable() {
- override def run() {
- for (image <- images) {
- debug(s"Adding image ${image.toString} to app")
- getImageTilePane.getChildren.add(ImageTileFactory.get(image, getImageTilePane))
- }
- }
- })
- }
-
- def getImageTilePane: TilePane = {
- this.imageTilePane
- }
-
- @FXML
- def showSimilarImages(event: ActionEvent) = {
- resetPaginator()
- imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
-
- val f: Future[List[Image]] = Future {
- val similarImages = engine.getSimilarImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
- val tempImages = new mutable.MutableList[Image]()
- for (similarImage <- similarImages) {
- debug(s"Adding similar images ${similarImage.toString} to app")
- similarImage.similarImages.foreach(image => tempImages += image)
- }
- tempImages.toList
- }
-
- f onComplete {
- case Success(images) =>
- info(s"Displaying ${images.length} similar images")
- Platform.runLater(new Runnable() {
- override def run() {
- setPagesContent(images)
- showPage(0)
- }
- })
- case Failure(t) =>
- error("An Error Occurred", t)
- }
- }
-
- /**
- * Show a plain text utility dialog
- *
- * @param message Message to display
- * @param wrapWidth When to wrap
- * @param alignment How it should be aligned
- */
- def showUtilityDialog(title: String,
- message: String,
- wrapWidth: Double = 300.0,
- xOffset: Double = 25.0,
- yOffset: Double = 25.0,
- alignment: TextAlignment = TextAlignment.JUSTIFY) = {
- val dialog: Stage = new Stage()
- dialog.initStyle(StageStyle.UTILITY)
- val parent: Group = new Group()
-
- // fill the text box
- val messageText = new Text()
- messageText.setText(message)
- messageText.setWrappingWidth(wrapWidth)
- messageText.setX(xOffset)
- messageText.setY(yOffset)
- messageText.setTextAlignment(TextAlignment.JUSTIFY)
-
- parent.getChildren.add(messageText)
- val scene: Scene = new Scene(parent)
- dialog.setScene(scene)
- dialog.setResizable(false)
- dialog.setMinWidth(wrapWidth + xOffset * 2)
- dialog.setTitle(title)
- dialog.show()
- }
-
- def print(): String = {
- "This method works"
- }
+ // Engine
+ val engine: Engine = new ConcurrentEngine()
+ //Define controls
+ @FXML var rootPane: AnchorPane = null
+ @FXML var rootMenuBar: MenuBar = null
+ @FXML var scrollPane: ScrollPane = null
+ @FXML var imageTilePane: TilePane = null
+ @FXML var tagListView: ListView[String] = null
+ // Labels
+ @FXML var selectedDirectoryLabel: Label = null
+ @FXML var currentDirectoryLabel: Label = null
+ @FXML var progressLabel: Label = null
+ // Others
+ @FXML var progressBar: ProgressBar = null
+ @FXML var paginator: Pagination = null
+ @FXML var doRecursiveProcessing: CheckBox = null
+ // Current State
+ var currentDirectory: String = "."
+ var currentImages: List[Image] = List[Image]()
+
+ @FXML def initialize() = {
+ if (PropertiesService.has("app.ui.lastPath")) {
+ currentDirectory = PropertiesService.get("app.ui.lastPath", ".")
+ selectedDirectoryLabel.setText(PropertiesService.get("app.ui.lastPath", ""))
+ }
+
+ //setup the engine listener
+ val system: ActorSystem = AppConfig.getAppActorSystem
+ val guiListenerProps: Props = Props.create(classOf[GUIEngineListener])
+ val guiListener: ActorRef = system.actorOf(guiListenerProps)
+ // configure the listener
+ guiListener ! SetupListener(progressBar, progressLabel)
+ // tell the engine to use our listener
+ this.engine.setSearchedListener(guiListener)
+ this.engine.setProcessedListener(guiListener)
+ this.engine.setSimilarityListener(guiListener)
+ // Initialize the progress label
+ guiListener ! SubmitMessage("Initialized System... Ready!")
+
+ // set the default images per page if it doesn't exist yet
+ if (!PropertiesService.has("app.ui.thumbsPerPage")) {
+ PropertiesService.set("app.ui.thumbsPerPage", "100")
+ }
+
+ //setup the paginator
+ //font size doesn't increase the size of the buttons
+ //paginator.setStyle("-fx-font-size:13;")
+ // configure the page factory
+ paginator.setPageFactory(new Callback[Integer, Node]() {
+ override def call(pageIndex: Integer): Node = {
+ // do all of our display logic
+ showPage(pageIndex)
+ // override behavior to display anything
+ new VBox()
+ }
+ })
+
+ //override the imageTilePane
+ debug("Replacing the default TilePane with a custom ImageTilePane")
+ val newImageTilePane = new ImageTilePane()
+ newImageTilePane.setHgap(this.imageTilePane.getHgap)
+ newImageTilePane.setVgap(this.imageTilePane.getVgap)
+ newImageTilePane.setMinHeight(this.imageTilePane.getMinHeight)
+ newImageTilePane.setMinWidth(this.imageTilePane.getMinWidth)
+ newImageTilePane.setMaxHeight(this.imageTilePane.getMaxHeight)
+ newImageTilePane.setMaxWidth(this.imageTilePane.getMaxWidth)
+ newImageTilePane.setPrefColumns(this.imageTilePane.getPrefColumns)
+ newImageTilePane.setPrefRows(this.imageTilePane.getPrefRows)
+ //newImageTilePane.setPrefTileHeight(this.imageTilePane.getPrefTileHeight)
+ //newImageTilePane.setPrefTileWidth(this.imageTilePane.getPrefTileWidth)
+ newImageTilePane.setTileAlignment(this.imageTilePane.getTileAlignment)
+ debug("Assigning the the new ImageTilePane to the ScrollPane")
+ this.scrollPane.setContent(newImageTilePane)
+ this.imageTilePane = newImageTilePane
+
+ //test
+ //val testImage = new Image()
+ //testImage.setThumbnailPath("test.jpg")
+ //testImage.setImagePath("test.jpg")
+ //val testImageList = new mutable.MutableList[Image]
+ //for (i <- 1 to 100) {
+ // testImageList += testImage
+ //}
+ //setPagesContent(testImageList.toList)
+ //showPage(0)
+ //val list = FXCollections.observableArrayList[String]()
+ //for (i <- 1 to 100) {
+ // list.add(s"test-item ${i}")
+ //}
+ //tagListView.setItems(list)
+ }
+
+ //region MenuItem Actions
+
+ @FXML
+ def helpAction(event: ActionEvent) = {
+ showExternalHTMLUtilityDialog("http://www.sothr.com")
+ }
+
+ def showExternalHTMLUtilityDialog(url: String) = {
+ val dialog: Stage = new Stage()
+ dialog.initStyle(StageStyle.UTILITY)
+ val parent: Group = new Group()
+
+ //setup the HTML view
+ val htmlView = new WebView
+ htmlView.getEngine.load(url)
+ //htmlView.setMinWidth(width)
+ //htmlView.setMinHeight(height)
+ //htmlView.setPrefWidth(width)
+ //htmlView.setPrefHeight(height)
+ parent.getChildren.add(htmlView)
+
+ val scene: Scene = new Scene(parent)
+ dialog.setScene(scene)
+ dialog.setResizable(false)
+ dialog.setTitle(htmlView.getEngine.getTitle)
+ dialog.show()
+ }
+
+ @FXML
+ def aboutAction(event: ActionEvent) = {
+ debug("Displaying about screen")
+ var aboutMessage = "Simple About Message"
+ try {
+
+ val scanner = new Scanner(ResourceLoader.get().getResourceStream("documents/about.md"))
+ aboutMessage = ""
+ while (scanner.hasNextLine) {
+ aboutMessage += scanner.nextLine().trim() + "\n"
+ }
+
+ debug(s"Parsed About Message: '$aboutMessage'")
+
+ } catch {
+ case ioe: IOException =>
+ error("Unable to read about file")
+ }
+
+ showMarkdownUtilityDialog("About", aboutMessage, 400.0, 300.0)
+ debug("Showing About Dialog")
+ }
+
+ //endregion
+
+ //region buttons
+
+ //todo: show a dialog that is rendered from markdown content
+ def showMarkdownUtilityDialog(title: String, markdown: String, width: Double = 800.0, height: Double = 600.0) = {
+ val htmlBody = new Markdown4jProcessor().process(markdown)
+ showHTMLUtilityDialog(title, htmlBody, width, height)
+ }
+
+ /**
+ * Render HTML content to a utility dialog. No input or output, just raw rendered content through a webkit engine.
+ *
+ * @param title Title of the dialog
+ * @param htmlBody Body to render
+ * @param width Desired width of the dialog
+ * @param height Desired height of the dialog
+ */
+ def showHTMLUtilityDialog(title: String, htmlBody: String, width: Double = 800.0, height: Double = 600.0) = {
+ val dialog: Stage = new Stage()
+ dialog.initStyle(StageStyle.UTILITY)
+ val parent: Group = new Group()
+
+ //setup the HTML view
+ val htmlView = new WebView
+ htmlView.getEngine.loadContent(htmlBody)
+ htmlView.setMinWidth(width)
+ htmlView.setMinHeight(height)
+ htmlView.setPrefWidth(width)
+ htmlView.setPrefHeight(height)
+ parent.getChildren.add(htmlView)
+
+ val scene: Scene = new Scene(parent)
+ dialog.setScene(scene)
+ dialog.setResizable(false)
+ dialog.setTitle(title)
+ dialog.show()
+ }
+
+ @FXML
+ def closeAction(event: ActionEvent) = {
+ debug("Closing application from the menu bar")
+ val stage: Stage = this.rootMenuBar.getScene.getWindow.asInstanceOf[Stage]
+ stage.close()
+ }
+
+ //endregion
+
+ //region pagination
+
+ @FXML
+ def browseFolders(event: ActionEvent) = {
+ val chooser = new DirectoryChooser()
+ chooser.setTitle("ImageTools Browser")
+
+ try {
+ val defaultDirectory = new File(currentDirectory)
+ chooser.setInitialDirectory(defaultDirectory)
+ val window = this.rootPane.getScene.getWindow
+ val selectedDirectory = chooser.showDialog(window)
+ info(s"Selected Directory: ${selectedDirectory.getAbsolutePath}")
+ selectedDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
+
+ currentDirectory = selectedDirectory.getAbsolutePath
+ PropertiesService.set("app.ui.lastPath", selectedDirectory.getAbsolutePath)
+ this.currentDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
+ } catch {
+ // fall back on the default because the directory we tried probably didn't exist
+ case iae: IllegalArgumentException =>
+ logger.error("The old directory no longer exists", iae)
+ chooser.setInitialDirectory(null)
+ val window = this.rootPane.getScene.getWindow
+ val selectedDirectory = chooser.showDialog(window)
+ info(s"Selected Directory: ${selectedDirectory.getAbsolutePath}")
+ selectedDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
+
+ currentDirectory = selectedDirectory.getAbsolutePath
+ PropertiesService.set("app.ui.lastPath", selectedDirectory.getAbsolutePath)
+ this.currentDirectoryLabel.setText(selectedDirectory.getAbsolutePath)
+ }
+ }
+
+ @FXML
+ def showAllImages(event: ActionEvent) = {
+ resetPaginator()
+ getImageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
+ val f: Future[List[Image]] = Future {
+ val images = engine.getImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
+ images.sortWith((x, y) => x.imagePath < y.imagePath)
+ }
+
+ f onComplete {
+ case Success(images) =>
+ info(s"Displaying ${images.length} images")
+ // This is used so that JavaFX updates on the proper thread
+ // This is important since UI updates can only happen on that thread
+ Platform.runLater(new Runnable() {
+ override def run() {
+ setPagesContent(images)
+ showPage(0)
+ }
+ })
+ case Failure(t) =>
+ error("An Error Occurred", t)
+ }
+ }
+
+ def resetPaginator() = {
+ this.paginator.setDisable(true)
+ this.paginator.setPageCount(1)
+ }
+
+ //endregion
+
+ def setPagesContent(images: List[Image]) = {
+ this.currentImages = images
+ //set the appropriate size for the pagination
+ val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt
+ val pageNum = Math.ceil(this.currentImages.size.toFloat / itemsPerPage).toInt
+ this.paginator.setPageCount(pageNum)
+ this.paginator.setDisable(false)
+ }
+
+ //todo: include a templating engine for rendering information
+
+ def showPage(pageIndex: Integer) = {
+ val itemsPerPage = PropertiesService.get("app.ui.thumbsPerPage", "100").toInt
+ val startIndex = pageIndex * itemsPerPage
+ val endIndex = if ((startIndex + itemsPerPage) > this.currentImages.size) this.currentImages.length else startIndex + itemsPerPage
+ //clear any selections
+ getImageTilePane.asInstanceOf[ImageTilePane].clearSelection()
+ //clear and populate the scrollpane
+ getImageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
+ val images = this.currentImages.slice(startIndex, endIndex)
+ Platform.runLater(new Runnable() {
+ override def run() {
+ for (image <- images) {
+ debug(s"Adding image ${image.toString} to app")
+ getImageTilePane.getChildren.add(ImageTileFactory.get(image, getImageTilePane))
+ }
+ }
+ })
+ }
+
+ def getImageTilePane: TilePane = {
+ this.imageTilePane
+ }
+
+ @FXML
+ def showSimilarImages(event: ActionEvent) = {
+ resetPaginator()
+ imageTilePane.getChildren.setAll(new java.util.ArrayList[Node]())
+
+ val f: Future[List[Image]] = Future {
+ val similarImages = engine.getSimilarImagesForDirectory(currentDirectory, recursive = doRecursiveProcessing.isSelected)
+ val tempImages = new mutable.MutableList[Image]()
+ for (similarImage <- similarImages) {
+ debug(s"Adding similar images ${similarImage.toString} to app")
+ similarImage.similarImages.foreach(image => tempImages += image)
+ }
+ tempImages.toList
+ }
+
+ f onComplete {
+ case Success(images) =>
+ info(s"Displaying ${images.length} similar images")
+ Platform.runLater(new Runnable() {
+ override def run() {
+ setPagesContent(images)
+ showPage(0)
+ }
+ })
+ case Failure(t) =>
+ error("An Error Occurred", t)
+ }
+ }
+
+ /**
+ * Show a plain text utility dialog
+ *
+ * @param message Message to display
+ * @param wrapWidth When to wrap
+ * @param alignment How it should be aligned
+ */
+ def showUtilityDialog(title: String,
+ message: String,
+ wrapWidth: Double = 300.0,
+ xOffset: Double = 25.0,
+ yOffset: Double = 25.0,
+ alignment: TextAlignment = TextAlignment.JUSTIFY) = {
+ val dialog: Stage = new Stage()
+ dialog.initStyle(StageStyle.UTILITY)
+ val parent: Group = new Group()
+
+ // fill the text box
+ val messageText = new Text()
+ messageText.setText(message)
+ messageText.setWrappingWidth(wrapWidth)
+ messageText.setX(xOffset)
+ messageText.setY(yOffset)
+ messageText.setTextAlignment(TextAlignment.JUSTIFY)
+
+ parent.getChildren.add(messageText)
+ val scene: Scene = new Scene(parent)
+ dialog.setScene(scene)
+ dialog.setResizable(false)
+ dialog.setMinWidth(wrapWidth + xOffset * 2)
+ dialog.setTitle(title)
+ dialog.show()
+ }
+
+ def print(): String = {
+ "This method works"
+ }
}
//region EngineListener
case class SetupListener(progressBar: ProgressBar, progressLabel: Label)
/**
- * Actor for logging output information
- */
+ * Actor for logging output information
+ */
class GUIEngineListener extends EngineListener with ActorLogging {
- var progressBar: javafx.scene.control.ProgressBar = null
- var progressLabel: javafx.scene.control.Label = null
-
- var isStarted = false
- var isFinished = false
-
- override def receive: Actor.Receive = {
- case command: SetupListener => setupListener(command)
- case command: SubmitMessage => handleMessage(command)
- case command: ScannedFileCount => handleScannedFileCount(command)
- case command: ComparedFileCount => handleComparedFileCount(command)
- case _ => log.info("received unknown message")
- }
-
- def setupListener(command: SetupListener) = {
- this.progressBar = command.progressBar
- this.progressLabel = command.progressLabel
- }
-
- override def handleComparedFileCount(command: ComparedFileCount): Unit = {
- Platform.runLater(new Runnable() {
- override def run(): Unit = {
- if (command.message != null) {
- log.debug(command.message)
- progressLabel.setText(command.message)
- } else {
- progressLabel.setText(s"Processed ${command.count}/${command.total}")
- }
- log.debug("Processed {}/{}", command.count, command.total)
- progressBar.setProgress(command.count.toFloat / command.total)
- }
- })
- }
-
- override def handleScannedFileCount(command: ScannedFileCount): Unit = {
- Platform.runLater(new Runnable() {
- override def run(): Unit = {
- if (command.message != null) {
- log.debug(command.message)
- progressLabel.setText(command.message)
- } else {
- progressLabel.setText(s"Scanned ${command.count}/${command.total} For Similarities")
- }
- log.debug("Scanned {}/{} For Similarities", command.count, command.total)
- progressBar.setProgress(command.count.toFloat / command.total)
- }
- })
- }
-
- override def handleMessage(command: SubmitMessage): Unit = {
- Platform.runLater(new Runnable() {
- override def run(): Unit = {
- log.debug(command.message)
- progressLabel.setText(command.message)
- }
- })
- }
+ var progressBar: javafx.scene.control.ProgressBar = null
+ var progressLabel: javafx.scene.control.Label = null
+
+ var isStarted = false
+ var isFinished = false
+
+ override def receive: Actor.Receive = {
+ case command: SetupListener => setupListener(command)
+ case command: SubmitMessage => handleMessage(command)
+ case command: ScannedFileCount => handleScannedFileCount(command)
+ case command: ComparedFileCount => handleComparedFileCount(command)
+ case _ => log.info("received unknown message")
+ }
+
+ def setupListener(command: SetupListener) = {
+ this.progressBar = command.progressBar
+ this.progressLabel = command.progressLabel
+ }
+
+ override def handleComparedFileCount(command: ComparedFileCount): Unit = {
+ Platform.runLater(new Runnable() {
+ override def run(): Unit = {
+ if (command.message != null) {
+ log.debug(command.message)
+ progressLabel.setText(command.message)
+ } else {
+ progressLabel.setText(s"Processed ${command.count}/${command.total}")
+ }
+ log.debug("Processed {}/{}", command.count, command.total)
+ progressBar.setProgress(command.count.toFloat / command.total)
+ }
+ })
+ }
+
+ override def handleScannedFileCount(command: ScannedFileCount): Unit = {
+ Platform.runLater(new Runnable() {
+ override def run(): Unit = {
+ if (command.message != null) {
+ log.debug(command.message)
+ progressLabel.setText(command.message)
+ } else {
+ progressLabel.setText(s"Scanned ${command.count}/${command.total} For Similarities")
+ }
+ log.debug("Scanned {}/{} For Similarities", command.count, command.total)
+ progressBar.setProgress(command.count.toFloat / command.total)
+ }
+ })
+ }
+
+ override def handleMessage(command: SubmitMessage): Unit = {
+ Platform.runLater(new Runnable() {
+ override def run(): Unit = {
+ log.debug(command.message)
+ progressLabel.setText(command.message)
+ }
+ })
+ }
}
-
-//endregion
+//endregion
\ No newline at end of file
diff --git a/gui/src/main/scala/com/sothr/imagetools/ui/util/FileUtil.scala b/gui/src/main/scala/com/sothr/imagetools/ui/util/FileUtil.scala
index 07fedc8..1e52107 100644
--- a/gui/src/main/scala/com/sothr/imagetools/ui/util/FileUtil.scala
+++ b/gui/src/main/scala/com/sothr/imagetools/ui/util/FileUtil.scala
@@ -7,24 +7,24 @@ import com.sothr.imagetools.engine.util.PropertiesService
import grizzled.slf4j.Logging
/**
- * Created by Drew Short on 8/31/2014.
- */
+ * Created by Drew Short on 8/31/2014.
+ */
object FileUtil extends Logging {
- def openInEditor(file: File) = {
- PropertiesService.OS.toLowerCase match {
- // Open file on windows
- case os if os.startsWith("windows") => openFileWindows(file)
- case os if os.startsWith("linux") => openFileLinux(file)
- case default => error(s"Do not know how to open editor for OS: ${PropertiesService.OS}, ${PropertiesService.OS_VERSION}, ${PropertiesService.OS_ARCH}")
- }
- }
+ def openInEditor(file: File) = {
+ PropertiesService.OS.toLowerCase match {
+ // Open file on windows
+ case os if os.startsWith("windows") => openFileWindows(file)
+ case os if os.startsWith("linux") => openFileLinux(file)
+ case default => error(s"Do not know how to open editor for OS: ${PropertiesService.OS}, ${PropertiesService.OS_VERSION}, ${PropertiesService.OS_ARCH}")
+ }
+ }
- private def openFileWindows(file: File) = {
- Desktop.getDesktop.open(file)
- }
+ private def openFileWindows(file: File) = {
+ Desktop.getDesktop.open(file)
+ }
- private def openFileLinux(file: File) = {
- Runtime.getRuntime.exec(s"xdg-open ${file.getAbsolutePath}")
- }
-}
+ private def openFileLinux(file: File) = {
+ Runtime.getRuntime.exec(s"xdg-open ${file.getAbsolutePath}")
+ }
+}
\ No newline at end of file
diff --git a/hash/pom.xml b/hash/pom.xml
index e864b52..b9b7245 100644
--- a/hash/pom.xml
+++ b/hash/pom.xml
@@ -1,6 +1,6 @@
-
com.sothr.imagetools
diff --git a/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala b/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala
index 6230a71..aa45f88 100644
--- a/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala
+++ b/hash/src/main/scala/com/sothr/imagetools/hash/HashService.scala
@@ -37,10 +37,6 @@ object HashService extends Logging {
imagePath)
}
- def getPrecisionMap(precisionSet: Set[Int], grayImage: BufferedImage): Map[Int, Array[Array[Int]]] = {
- precisionSet.map(p => p -> getImageData(p, grayImage, alreadyGray = true))(collection.breakOut)
- }
-
/**
* Given hash settings, a buffered image and an image path, calculate the perceptual hashes
*
@@ -86,18 +82,8 @@ object HashService extends Logging {
hashes
}
- def getSHA1(filePath: String): String = {
- managed(new FileInputStream(filePath)) acquireAndGet {
- input => DigestUtils.sha1Hex(input)
- }
- }
-
- def getPrecisionSet(ahashSettings: HashSetting, dhashSettings: HashSetting, phashSettings: HashSetting): Set[Int] = {
- Set(
- if (ahashSettings.use) Option(ahashSettings.precision) else None,
- if (dhashSettings.use) Option(dhashSettings.precision) else None,
- if (phashSettings.use) Option(phashSettings.precision) else None
- ).flatten
+ def getPrecisionMap(precisionSet: Set[Int], grayImage: BufferedImage): Map[Int, Array[Array[Int]]] = {
+ precisionSet.map(p => p -> getImageData(p, grayImage, alreadyGray = true))(collection.breakOut)
}
def getImageData(precision: Int, image: BufferedImage, alreadyGray: Boolean): Array[Array[Int]] = {
@@ -113,6 +99,20 @@ object HashService extends Logging {
ImageUtil.getImageData(resizedImage)
}
+ def getSHA1(filePath: String): String = {
+ managed(new FileInputStream(filePath)) acquireAndGet {
+ input => DigestUtils.sha1Hex(input)
+ }
+ }
+
+ def getPrecisionSet(ahashSettings: HashSetting, dhashSettings: HashSetting, phashSettings: HashSetting): Set[Int] = {
+ Set(
+ if (ahashSettings.use) Option(ahashSettings.precision) else None,
+ if (dhashSettings.use) Option(dhashSettings.precision) else None,
+ if (phashSettings.use) Option(phashSettings.precision) else None
+ ).flatten
+ }
+
/**
* Simpler function that only works with the imageData and the hashFunction
*
diff --git a/parent/pom.xml b/parent/pom.xml
index 48a4e25..e75340f 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -1,460 +1,460 @@
- 4.0.0
+ 4.0.0
- com.sothr.imagetools
- parent
- 1.0.1
+ com.sothr.imagetools
+ parent
+ 1.0.1
- pom
- Image-Tools-Parent
+ pom
+ Image-Tools-Parent
-
-
- Sothr Maven Public Proxy
- mvn-public-sothr-proxy
- https://nexus.sothr.com/repository/maven-public/
-
-
- Sothr Releases
- sothr-nexus-releases
- https://nexus.sothr.com/repository/maven-releases/
-
- true
- always
- fail
-
-
- false
- never
- warn
-
-
-
- Sothr Snapshots
- sothr-nexus-snapshots
- https://nexus.sothr.com/repository/maven-snapshots/
-
- false
- never
- warn
-
-
- true
- daily
- fail
-
-
-
+
+
+ Sothr Maven Public Proxy
+ mvn-public-sothr-proxy
+ https://nexus.sothr.com/repository/maven-public/
+
+
+ Sothr Releases
+ sothr-nexus-releases
+ https://nexus.sothr.com/repository/maven-releases/
+
+ true
+ always
+ fail
+
+
+ false
+ never
+ warn
+
+
+
+ Sothr Snapshots
+ sothr-nexus-snapshots
+ https://nexus.sothr.com/repository/maven-snapshots/
+
+ false
+ never
+ warn
+
+
+ true
+ daily
+ fail
+
+
+
-
-
- sothr-nexus-releases
- https://nexus.sothr.com/repository/maven-releases/
-
-
- sothr-nexus-snapshots
- https://nexus.sothr.com/repository/maven-snapshots/
-
-
+
+
+ sothr-nexus-releases
+ https://nexus.sothr.com/repository/maven-releases/
+
+
+ sothr-nexus-snapshots
+ https://nexus.sothr.com/repository/maven-snapshots/
+
+
-
-
- sonatype-releases
- http://oss.sonatype.org/content/repositories/releases
-
-
- clojars.org
- http://clojars.org/repo
-
-
+
+
+ sonatype-releases
+ http://oss.sonatype.org/content/repositories/releases
+
+
+ clojars.org
+ http://clojars.org/repo
+
+
-
-
- 0.2.0
- 0.1.3
-
- UTF-8
- 1.8
- 2.12
- 2.12.4
- 4.12
- 3.0.4
- 1.2.3
- 1.7.25
- 1.3.2
- 2.5.8
- 1.1
- 2.10.4
- 1.4
- 1.11
- 2.4.0
- 1.3.2
- [0.4, 0.5)
- 1.4.196
- 4.3.11.Final
- 2.6.11
- 2.2-cj-1.1
- 2.0
- 3.3.2
-
+
+
+ 0.2.0
+ 0.1.3
+
+ UTF-8
+ 1.8
+ 2.12
+ 2.12.4
+ 4.12
+ 3.0.4
+ 1.2.3
+ 1.7.25
+ 1.3.2
+ 2.5.8
+ 1.1
+ 2.10.4
+ 1.4
+ 1.11
+ 2.4.0
+ 1.3.2
+ [0.4, 0.5)
+ 1.4.196
+ 4.3.11.Final
+ 2.6.11
+ 2.2-cj-1.1
+ 2.0
+ 3.3.2
+
-
-
-
- com.sothr.imagetools
- hash
- ${imagetools.hash.version}
-
-
- com.sothr.imagetools
- engine
- ${imagetools.engine.version}
-
-
- junit
- junit
- ${lib.junit.version}
- test
-
-
- org.scalatest
- scalatest_${scala.binary.version}
- ${lib.scalatest.version}
- test
-
-
- ch.qos.logback
- logback-core
- ${lib.logback.version}
-
-
- ch.qos.logback
- logback-classic
- ${lib.logback.version}
-
-
- ch.qos.logback
- logback-access
- ${lib.logback.version}
-
-
- org.slf4j
- slf4j-api
- ${lib.slf4j.version}
-
-
- org.clapper
- grizzled-slf4j_${scala.binary.version}
- ${lib.grizzled-slf4j.version}
-
-
- org.scala-lang
- scala-library
- ${lib.scala-library.version}
-
-
- net.coobird
- thumbnailator
- ${lib.thumbnailator.version}
-
-
- com.typesafe
- config
- ${lib.typesafe-config.version}
-
-
- net.sourceforge.jtransforms
- jtransforms
- ${lib.jtransforms.version}
-
-
- commons-cli
- commons-cli
- ${lib.commons-cli.version}
-
-
- commons-codec
- commons-codec
- ${lib.commons-codec.version}
-
-
- javax.transaction
- jta
- ${lib.jta.version}
-
-
- net.sf.ehcache
- ehcache
- ${lib.ehcache.version}
-
-
- com.typesafe.akka
- akka-actor_${scala.binary.version}
- ${lib.akka.version}
-
-
- com.typesafe.akka
- akka-slf4j_${scala.binary.version}
- ${lib.akka.version}
-
-
- com.h2database
- h2
- ${lib.h2database.version}
-
-
- org.hibernate
- hibernate-core
- ${lib.hibernate.version}
-
-
- org.hibernate
- hibernate-ehcache
- ${lib.hibernate.version}
-
-
- net.sf.ehcache
- ehcache-core
- ${lib.hibernate.ehcache.version}
-
-
- org.hibernate
- hibernate-c3p0
- ${lib.hibernate.version}
-
-
- org.commonjava.googlecode.markdown4j
- markdown4j
- ${lib.markdown4j.version}
-
-
- com.jsuereth
- scala-arm_${scala.binary.version}
- ${lib.scala-arm.version}
-
-
- com.twelvemonkeys.imageio
- imageio-jpeg
- ${lib.twelvemonkeys.imageio.version}
-
-
-
+
+
+
+ com.sothr.imagetools
+ hash
+ ${imagetools.hash.version}
+
+
+ com.sothr.imagetools
+ engine
+ ${imagetools.engine.version}
+
+
+ junit
+ junit
+ ${lib.junit.version}
+ test
+
+
+ org.scalatest
+ scalatest_${scala.binary.version}
+ ${lib.scalatest.version}
+ test
+
+
+ ch.qos.logback
+ logback-core
+ ${lib.logback.version}
+
+
+ ch.qos.logback
+ logback-classic
+ ${lib.logback.version}
+
+
+ ch.qos.logback
+ logback-access
+ ${lib.logback.version}
+
+
+ org.slf4j
+ slf4j-api
+ ${lib.slf4j.version}
+
+
+ org.clapper
+ grizzled-slf4j_${scala.binary.version}
+ ${lib.grizzled-slf4j.version}
+
+
+ org.scala-lang
+ scala-library
+ ${lib.scala-library.version}
+
+
+ net.coobird
+ thumbnailator
+ ${lib.thumbnailator.version}
+
+
+ com.typesafe
+ config
+ ${lib.typesafe-config.version}
+
+
+ net.sourceforge.jtransforms
+ jtransforms
+ ${lib.jtransforms.version}
+
+
+ commons-cli
+ commons-cli
+ ${lib.commons-cli.version}
+
+
+ commons-codec
+ commons-codec
+ ${lib.commons-codec.version}
+
+
+ javax.transaction
+ jta
+ ${lib.jta.version}
+
+
+ net.sf.ehcache
+ ehcache
+ ${lib.ehcache.version}
+
+
+ com.typesafe.akka
+ akka-actor_${scala.binary.version}
+ ${lib.akka.version}
+
+
+ com.typesafe.akka
+ akka-slf4j_${scala.binary.version}
+ ${lib.akka.version}
+
+
+ com.h2database
+ h2
+ ${lib.h2database.version}
+
+
+ org.hibernate
+ hibernate-core
+ ${lib.hibernate.version}
+
+
+ org.hibernate
+ hibernate-ehcache
+ ${lib.hibernate.version}
+
+
+ net.sf.ehcache
+ ehcache-core
+ ${lib.hibernate.ehcache.version}
+
+
+ org.hibernate
+ hibernate-c3p0
+ ${lib.hibernate.version}
+
+
+ org.commonjava.googlecode.markdown4j
+ markdown4j
+ ${lib.markdown4j.version}
+
+
+ com.jsuereth
+ scala-arm_${scala.binary.version}
+ ${lib.scala-arm.version}
+
+
+ com.twelvemonkeys.imageio
+ imageio-jpeg
+ ${lib.twelvemonkeys.imageio.version}
+
+
+
-
-
-
- src/main/resources
- true
-
- **/*.conf
- **/*.properties
- **/*.info
- **/*.md
-
-
-
- src/main/resources
- false
-
- **/*.conf
- **/*.properties
- **/*.info
- **/*.md
-
-
-
+
+
+
+ src/main/resources
+ true
+
+ **/*.conf
+ **/*.properties
+ **/*.info
+ **/*.md
+
+
+
+ src/main/resources
+ false
+
+ **/*.conf
+ **/*.properties
+ **/*.info
+ **/*.md
+
+
+
-
-
- src/test/resources
- true
-
- **/*.conf
- **/*.properties
- **/*.info
- **/*.md
-
-
-
- src/test/resources
- false
-
- **/*.conf
- **/*.properties
- **/*.info
- **/*.md
-
-
-
+
+
+ src/test/resources
+ true
+
+ **/*.conf
+ **/*.properties
+ **/*.info
+ **/*.md
+
+
+
+ src/test/resources
+ false
+
+ **/*.conf
+ **/*.properties
+ **/*.info
+ **/*.md
+
+
+
-
-
-
- net.alchim31.maven
- scala-maven-plugin
- 3.1.6
-
-
- org.apache.maven.plugins
- maven-resources-plugin
- 2.6
-
-
- org.apache.maven.plugins
- maven-source-plugin
- 3.0.1
-
-
-
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+ 3.1.6
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 2.6
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.0.1
+
+
+
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 2.7
-
- true
-
-
-
-
- com.code54.mojo
- buildversion-plugin
- 1.0.3
-
-
-
- set-properties
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 3.1
-
-
- ${jdk.version}
-
-
-
- compile
-
- compile
-
-
-
-
-
-
- net.alchim31.maven
- scala-maven-plugin
-
-
- scala-compile-first
- process-resources
-
- add-source
- compile
-
-
-
- scala-test-compile
- process-test-resources
-
- testCompile
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
- 2.6
-
-
- copy-dependencies
- package
-
- copy-dependencies
-
-
- runtime
-
- ${project.build.directory}/release/lib
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-resources-plugin
- 2.6
-
-
- copy-resources
- initialize
-
- copy-resources
-
-
- ${project.build.directory}
-
-
- src/includes
- true
-
-
-
-
-
- copy-resources-package
- package
-
- copy-resources
-
-
- ${project.build.directory}/release
-
-
- src/includes
- true
-
- version.info
- name.info
-
-
-
-
-
-
-
-
-
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.7
+
+ true
+
+
+
+
+ com.code54.mojo
+ buildversion-plugin
+ 1.0.3
+
+
+
+ set-properties
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+
+ ${jdk.version}
+
+
+
+ compile
+
+ compile
+
+
+
+
+
+
+ net.alchim31.maven
+ scala-maven-plugin
+
+
+ scala-compile-first
+ process-resources
+
+ add-source
+ compile
+
+
+
+ scala-test-compile
+ process-test-resources
+
+ testCompile
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 2.6
+
+
+ copy-dependencies
+ package
+
+ copy-dependencies
+
+
+ runtime
+
+ ${project.build.directory}/release/lib
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 2.6
+
+
+ copy-resources
+ initialize
+
+ copy-resources
+
+
+ ${project.build.directory}
+
+
+ src/includes
+ true
+
+
+
+
+
+ copy-resources-package
+ package
+
+ copy-resources
+
+
+ ${project.build.directory}/release
+
+
+ src/includes
+ true
+
+ version.info
+ name.info
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 3355ebe..00b15c3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,29 +1,29 @@
- 4.0.0
+ 4.0.0
- com.sothr.imagetools
- build
- 0.1.3-DEV
- pom
+ com.sothr.imagetools
+ build
+ 0.1.3-DEV
+ pom
- Image-Tools
- An image collection management utility
- http://imagetools.sothr.com
-
- Sothr Software
-
+ Image-Tools
+ An image collection management utility
+ http://imagetools.sothr.com
+
+ Sothr Software
+
-
- true
-
+
+ true
+
-
- parent
- hash
- engine
- cli
- gui
-
+
+ parent
+ hash
+ engine
+ cli
+ gui
+