Allows multiple Scala scripts to be included in a single plugin

When the 'plugin-version.scala' under /plugins is a directory 
all the Scala scripts inside the directory tree are loaded
inside the same plugin environment.

This allows to save precious PermGen JVM Heap and reuse
the same plugin name space for multiple Scala scripts.
Additionally allows to have a much better organisation of
larger set of scripts in packages.

Change-Id: Ic885d4c8d89ce93f801fc642869e21378abe4fd0
diff --git a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java
index 0651148..27b7490 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginProvider.java
@@ -48,7 +48,7 @@
  */
 @Singleton
 class ScalaPluginProvider implements ServerPluginProvider {
-  private static final String SCALA_EXTENSION = ".scala";
+  public static final String SCALA_EXTENSION = ".scala";
 
   private final Provider<ScalaPluginScriptEngine> scriptEngineProvider;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScriptEngine.java b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScriptEngine.java
index 371958e..91b8eb1 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScriptEngine.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/scripting/scala/ScalaPluginScriptEngine.java
@@ -15,6 +15,8 @@
 
 import static scala.collection.JavaConversions.asScalaBuffer;
 
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 import com.google.inject.Inject;
 
@@ -38,8 +40,15 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.nio.file.FileVisitOption;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.Set;
 
@@ -151,21 +160,62 @@
 
   public Set<Class<?>> eval(File scalaFile) throws IOException,
       ClassNotFoundException {
+    if (scalaFile.isFile()) {
+      return evalFiles(Arrays.asList(scalaFile));
+    } else if (scalaFile.isDirectory()) {
+      return evalDirectory(scalaFile);
+    } else {
+      throw new IOException("File " + scalaFile
+          + " is not a supported for loading Scala scripts");
+    }
+  }
+
+  private Set<Class<?>> evalDirectory(File scalaFile) throws IOException,
+      ClassNotFoundException {
+    final List<File> scalaFiles = Lists.newArrayList();
+
+    Files.walkFileTree(scalaFile.toPath(),
+        EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
+        new SimpleFileVisitor<Path>() {
+          @Override
+          public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
+              throws IOException {
+            File file = path.toFile();
+            String fileName = file.getName();
+            if (file.isFile() && fileName.endsWith(ScalaPluginProvider.SCALA_EXTENSION)) {
+              scalaFiles.add(file);
+            }
+            return FileVisitResult.CONTINUE;
+          }
+        });
+    return evalFiles(scalaFiles);
+  }
+
+  private Set<Class<?>> evalFiles(List<File> scalaFiles) throws IOException,
+      ClassNotFoundException {
     Set<Class<?>> classes = Sets.newHashSet();
 
-    SourceFile sourceFile =
-        new BatchSourceFile(scalaFile.getName(), readScalaFile(scalaFile));
+    List<SourceFile> scalaSourceFiles = Lists.transform(scalaFiles, new Function<File,SourceFile>() {
+      @Override
+      public SourceFile apply(File scalaFile) {
+        try {
+          return new BatchSourceFile(scalaFile.getName(), readScalaFile(scalaFile));
+        } catch (IOException e) {
+          throw new IllegalArgumentException("Cannot load scala file " + scalaFile, e);
+        }
+      }
+    });
     Run run = globalEngine.new Run();
     reporter.reset();
-    run.compileSources(asScalaBuffer(Arrays.asList(sourceFile)).toList());
+    run.compileSources(asScalaBuffer(scalaSourceFiles).toList());
     if (reporter.hasErrors()) {
-      LOG.error("Error compiling scala file " + scalaFile);
+      LOG.error("Error compiling scala files " + scalaFiles);
       LOG.error(reporter.getOutput());
-      throw new IOException("Invalid Scala file " + scalaFile);
+      throw new IOException("Invalid Scala files " + scalaFiles);
     } else {
       String output = reporter.getOutput();
       if(output.length() > 0) {
-        LOG.info("Scala file " + scalaFile + " loaded successfully");
+        LOG.info("Scala files " + scalaFiles + " loaded successfully");
         LOG.info(output);
       }
     }