This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch GROOVY-9468
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 0e0d10a9c0bcdce8ec66f4c06d9cbfece4115c18
Author: Daniel Sun <[email protected]>
AuthorDate: Sun Mar 15 02:50:45 2020 +0800

    GROOVY-9468: Support running as Java code in the Groovy console
---
 subprojects/groovy-console/build.gradle            |  1 +
 .../main/groovy/groovy/console/ui/Console.groovy   | 86 +++++++++++++++++++---
 .../groovy/groovy/console/ui/ConsoleActions.groovy | 13 ++++
 .../groovy/console/ui/view/BasicMenuBar.groovy     |  2 +
 .../groovy/console/ui/view/MacOSXMenuBar.groovy    |  2 +
 5 files changed, 95 insertions(+), 9 deletions(-)

diff --git a/subprojects/groovy-console/build.gradle 
b/subprojects/groovy-console/build.gradle
index 4b48627..e29322e 100644
--- a/subprojects/groovy-console/build.gradle
+++ b/subprojects/groovy-console/build.gradle
@@ -20,6 +20,7 @@ evaluationDependsOn(':groovy-swing')
 
 dependencies {
     api rootProject // AstBrowser has methods with Closure params...
+    implementation "com.github.javaparser:javaparser-core:$javaParserVersion"
     implementation project(':groovy-swing')
     implementation project(':groovy-templates')
     testImplementation project(':groovy-test')
diff --git 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy
index 6117100..9617d39 100644
--- 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy
+++ 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/Console.groovy
@@ -18,6 +18,11 @@
  */
 package groovy.console.ui
 
+
+import com.github.javaparser.StaticJavaParser
+import com.github.javaparser.ast.CompilationUnit
+import com.github.javaparser.ast.Modifier
+import com.github.javaparser.ast.body.TypeDeclaration
 import groovy.cli.internal.CliBuilderInternal
 import groovy.cli.internal.OptionAccessor
 import groovy.console.ui.text.FindReplaceUtility
@@ -35,6 +40,7 @@ import org.apache.groovy.antlr.LexerFrame
 import org.apache.groovy.io.StringBuilderWriter
 import org.apache.groovy.parser.antlr4.GroovyLangLexer
 import org.apache.groovy.parser.antlr4.GroovyLangParser
+import org.apache.groovy.util.JavaShell
 import org.apache.groovy.util.SystemUtil
 import org.codehaus.groovy.control.CompilerConfiguration
 import org.codehaus.groovy.control.ErrorCollector
@@ -1136,13 +1142,25 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
         return consoleControllers.find { it.consoleId == consoleId }
     }
 
+    private enum SourceType {
+        GROOVY, JAVA
+    }
+
+    void runJava(EventObject evt = null) {
+        doRun(evt, SourceType.JAVA)
+    }
+
     // actually run the script
     void runScript(EventObject evt = null) {
+        doRun(evt, SourceType.GROOVY)
+    }
+
+    private doRun(EventObject evt = null, SourceType st) {
         saveInputAreaContentHash()
         if (saveOnRun && scriptFile != null) {
-            if (fileSave(evt)) runScriptImpl(false)
+            if (fileSave(evt)) runScriptImpl(false, st)
         } else {
-            runScriptImpl(false)
+            runScriptImpl(false, st)
         }
     }
 
@@ -1180,11 +1198,20 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
         cc.getOptimizationOptions().get(CompilerConfiguration.INVOKEDYNAMIC)
     }
 
+    void runSelectedJava(EventObject evt = null) {
+        doRunSelected(evt, SourceType.JAVA)
+    }
+
     void runSelectedScript(EventObject evt = null) {
+        doRunSelected(evt, SourceType.GROOVY)
+    }
+
+    private void doRunSelected(EventObject evt = null, SourceType st) {
         saveInputAreaContentHash()
-        runScriptImpl(true)
+        runScriptImpl(true, st)
     }
 
+
     void addClasspathJar(EventObject evt = null) {
         def fc = new JFileChooser(currentClasspathJarDir)
         fc.fileSelectionMode = JFileChooser.FILES_ONLY
@@ -1251,7 +1278,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
         inputAreaContentHash = inputArea.getText().hashCode()
     }
 
-    private void runScriptImpl(boolean selected) {
+    private void runScriptImpl(boolean selected, SourceType st = 
SourceType.GROOVY) {
         if (scriptRunning) {
             statusLabel.text = 'Cannot run script now as a script is already 
running. Please wait or use "Interrupt Script" option.'
             return
@@ -1269,8 +1296,9 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
 
         // Print the input text
         if (showScriptInOutput) {
+            final promptPrfix = SourceType.JAVA === st ? 'java> ' : 'groovy> '
             for (line in record.getTextToRun(selected).tokenize('\n')) {
-                appendOutputNl('groovy> ', promptStyle)
+                appendOutputNl(promptPrfix, promptStyle)
                 appendOutput(line, commandStyle)
             }
             appendOutputNl(' \n', promptStyle)
@@ -1282,7 +1310,6 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
             try {
                 systemOutInterceptor.setConsoleId(this.getConsoleId())
                 SwingUtilities.invokeLater { showExecutingMessage() }
-                String name = scriptFile?.name ?: (DEFAULT_SCRIPT_NAME_START + 
scriptNameCounter++)
                 if (beforeExecution) {
                     beforeExecution()
                 }
@@ -1291,13 +1318,13 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
                     ClassLoader savedThreadContextClassLoader = 
Thread.currentThread().contextClassLoader
                     try {
                         Thread.currentThread().contextClassLoader = 
shell.classLoader
-                        result = shell.run(record.getTextToRun(selected), 
name, [])
+                        result = doRun(selected, st, record)
                     }
                     finally {
                         Thread.currentThread().contextClassLoader = 
savedThreadContextClassLoader
                     }
                 } else {
-                    result = shell.run(record.getTextToRun(selected), name, [])
+                    result = doRun(selected, st, record)
                 }
                 if (afterExecution) {
                     afterExecution()
@@ -1319,7 +1346,7 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
                     int delay = prefs.getInt('loopModeDelay', 
ConsolePreferences.DEFAULT_LOOP_MODE_DELAY_MILLIS)
                     Timer timer = new Timer(delay, {
                         if( inputAreaContentHash == 
inputArea.getText().hashCode() ) {
-                            runScriptImpl(selected)
+                            runScriptImpl(selected, st)
                         }
                     })
                     timer.repeats = false
@@ -1329,6 +1356,47 @@ class Console implements CaretListener, 
HyperlinkListener, ComponentListener, Fo
         }
     }
 
+    @CompileStatic
+    private Object doRun(boolean selected, SourceType st, HistoryRecord 
record) {
+        def result
+        def src = record.getTextToRun(selected)
+        if (SourceType.JAVA === st) {
+            Optional<String> optionalPrimaryClassName = 
findPrimaryClassName(src)
+            if (optionalPrimaryClassName.isPresent()) {
+                def js = new 
JavaShell(Thread.currentThread().contextClassLoader)
+                js.run(optionalPrimaryClassName.get(), src)
+            }
+        } else {
+            String name = ((File) scriptFile)?.name ?: 
(DEFAULT_SCRIPT_NAME_START + scriptNameCounter++)
+            result = shell.run(src, name, [])
+        }
+        return result
+    }
+
+    @CompileStatic
+    private static Optional<String> findPrimaryClassName(String javaSrc) {
+        List<TypeDeclaration<?>> result = new LinkedList<>()
+        CompilationUnit compilationUnit = StaticJavaParser.parse(javaSrc)
+
+        for (TypeDeclaration<?> td : compilationUnit.getTypes()) {
+            if (!(td.isTopLevelType() && td.isClassOrInterfaceDeclaration() && 
td.getModifiers().contains(Modifier.publicModifier()))) continue
+
+            if (td.fullyQualifiedName.isPresent()) {
+                result << td
+            }
+        }
+
+        String className = null
+        if (!result.isEmpty()) {
+            Optional<String> optionalClassName = 
result.get(0).getFullyQualifiedName()
+            if (optionalClassName.isPresent()) {
+                className = optionalClassName.get()
+            }
+        }
+
+        return Optional.ofNullable(className)
+    }
+
     void compileScript(EventObject evt = null) {
         if (scriptRunning) {
             statusLabel.text = 'Cannot compile script now as a script is 
already running. Please wait or use "Interrupt Script" option.'
diff --git 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy
 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy
index 57a3f19..fb05c2c 100644
--- 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy
+++ 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/ConsoleActions.groovy
@@ -204,6 +204,14 @@ runAction = action(
         shortDescription: 'Execute Groovy Script'
 )
 
+runJavaAction = action(
+        name: 'Run as Java',
+        closure: controller.&runJava,
+        mnemonic: 'J',
+        accelerator: shortcut('alt R'),
+        shortDescription: 'Execute Java Code'
+)
+
 loopModeAction = action(
         name: 'Loop Mode',
         closure: controller.&loopMode,
@@ -219,6 +227,11 @@ runSelectionAction = action(
         accelerator: shortcut('shift R')
 )
 
+runJavaSelectionAction = action(
+        name: 'Run Java Selection',
+        closure: controller.&runSelectedJava
+)
+
 addClasspathJar = action(
         name: 'Add Jar(s) to ClassPath',
         closure: controller.&addClasspathJar,
diff --git 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy
 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy
index 4760c66..8252e33 100644
--- 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy
+++ 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/BasicMenuBar.groovy
@@ -78,9 +78,11 @@ menuBar {
 
     menu(text: 'Script', mnemonic: 'S') {
         menuItem(runAction)
+        menuItem(runJavaAction)
         checkBoxMenuItem(loopModeAction, selected: controller.loopMode)
         checkBoxMenuItem(saveOnRunAction, selected: controller.saveOnRun)
         menuItem(runSelectionAction)
+        menuItem(runJavaSelectionAction)
         checkBoxMenuItem(threadInterruptAction, selected: 
controller.threadInterrupt)
         menuItem(interruptAction)
         menuItem(compileAction)
diff --git 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy
 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy
index 58a78a7..20a2cd9 100644
--- 
a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy
+++ 
b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/view/MacOSXMenuBar.groovy
@@ -130,9 +130,11 @@ menuBar {
 
     menu(text: 'Script', mnemonic: 'S') {
         menuItem(runAction, icon:null)
+        menuItem(runJavaAction, icon:null)
         checkBoxMenuItem(loopModeAction, selected: controller.loopMode)
         checkBoxMenuItem(saveOnRunAction, selected: controller.saveOnRun)
         menuItem(runSelectionAction, icon:null)
+        menuItem(runJavaSelectionAction, icon:null)
         checkBoxMenuItem(threadInterruptAction, selected: 
controller.threadInterrupt)
         menuItem(interruptAction, icon:null)
         menuItem(compileAction, icon:null)

Reply via email to