Index: src/main/groovy/ui/Console.groovy =================================================================== --- src/main/groovy/ui/Console.groovy (revision 15193) +++ src/main/groovy/ui/Console.groovy (working copy) @@ -178,7 +178,7 @@ } void newScript(ClassLoader parent, Binding binding) { - shell = new GroovyShell(parent, binding) + shell = new DefaultImportsGroovyShell(parent, binding) } static def frameConsoleDelegates = [ @@ -729,7 +729,12 @@ if(beforeExecution) { beforeExecution() } - def result = shell.run(record.getTextToRun(selected), name, []) + def result + if (!selected) { + result = shell.run(record.getTextToRun(selected), name, []) + } else { + result = shell.runSelected(record.getAllText(), record.getTextToRun(selected), name, []) + } if(afterExecution) { afterExecution() } Index: src/main/groovy/ui/DefaultImportsGroovyShell.groovy =================================================================== --- src/main/groovy/ui/DefaultImportsGroovyShell.groovy (revision 0) +++ src/main/groovy/ui/DefaultImportsGroovyShell.groovy (revision 0) @@ -0,0 +1,111 @@ +package groovy.ui + +import org.codehaus.groovy.control.SourceUnit +import org.codehaus.groovy.control.CompilationUnit +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.Phases +import org.codehaus.groovy.control.CompilationFailedException +import org.codehaus.groovy.ast.ImportNode +import org.codehaus.groovy.ast.ModuleNode +import org.codehaus.groovy.ast.ClassNode +import java.security.CodeSource + + +/** + * Helping class that allows to run selected part of script in context of whole script. + * + * @author Sergii Bondarenko + * + * Created on 2009-02-01 + */ +public class DefaultImportsGroovyShell extends GroovyShell { + public DefaultImportsGroovyShell() { + this(null, new Binding()) + } + + public DefaultImportsGroovyShell(Binding binding) { + this(null, binding) + } + + public DefaultImportsGroovyShell(CompilerConfiguration config) { + this(new Binding(), config) + } + + public DefaultImportsGroovyShell(Binding binding, CompilerConfiguration config) { + this(null, binding, config) + } + + public DefaultImportsGroovyShell(ClassLoader parent, Binding binding) { + this(parent, binding, CompilerConfiguration.DEFAULT) + } + + public DefaultImportsGroovyShell(ClassLoader parent) { + this(parent, new Binding(), CompilerConfiguration.DEFAULT) + } + public DefaultImportsGroovyShell(ClassLoader parent, Binding binding, final CompilerConfiguration config) { + super(parent, binding, config) + ClassLoader parentLoader = (parent!=null)?parent:GroovyShell.class.getClassLoader() + this.class.superclass.metaClass.setAttribute(this, 'loader', new DefaultImportsGroovyClassLoader(parentLoader,config)) + } + + public Object runSelected(String fullScriptText, String selectedText, String fileName, List list) throws CompilationFailedException { + try { + try { + fullSource = new GroovyCodeSource(fullScriptText, 'FullScript', '/groovy.shell') + loader = new GroovyClassLoader() + compilationUnit = new CompilationUnit(CompilerConfiguration.DEFAULT, fullSource.getCodeSource(), loader) + su = compilationUnit.addSource(fullSource.getName(), fullSource.getInputStream()) + compilationUnit.compile(Phases.CANONICALIZATION) + classLoader.defaultImports = su.AST.imports + classLoader.defaultImportPackages = su.AST.importPackages + classLoader.defaultStaticImportClasses = su.AST.staticImportClasses + classLoader.defaultStaticImportAliases = su.AST.staticImportAliases +// println su.AST.dump() + } catch (CompilationFailedException) { + //since we are trying to run selected part of script, compilation errors in whole script are silently ignored + } + return run(selectedText, fileName, list as String[]) + } finally { + classLoader.defaultImports = [] + } + } + +} + +private static class DefaultImportsGroovyClassLoader extends GroovyClassLoader { + List defaultImports = [] + List defaultImportPackages = [] + Map defaultStaticImportClasses = [:] + Map defaultStaticImportAliases = [:] + public DefaultImportsGroovyClassLoader() { + super() + } + public DefaultImportsGroovyClassLoader(ClassLoader loader, CompilerConfiguration config) { + super(loader, config) + } + protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) { + CompilationUnit cu = super.createCompilationUnit(config, source) + def importsOperation = new DefaultImportsOperation( + defaultImports:defaultImports, + defaultImportPackages:defaultImportPackages, + defaultStaticImportClasses:defaultStaticImportClasses, + defaultStaticImportAliases:defaultStaticImportAliases) + + cu.addPhaseOperation(importsOperation, Phases.CONVERSION) + return cu + } +} + +private static class DefaultImportsOperation extends CompilationUnit.SourceUnitOperation { + List defaultImports = [] + List defaultImportPackages = [] + Map defaultStaticImportClasses = [:] + Map defaultStaticImportAliases = [:] + public void call(SourceUnit source) throws CompilationFailedException { + ModuleNode module = source.getAST() + defaultImports.each{ if (module.getImport(it.alias) == null) module.addImport(it.alias, it.type) } + defaultImportPackages.each{ module.addImportPackage(it) } + defaultStaticImportClasses.each{ module.addStaticImportClass(it.key, it.value) } + defaultStaticImportAliases.each{ if (!module.staticImportAliases.containsKey(it.key)) module.addStaticMethodOrField(it.value, it.key, it.key) } + } +} Index: src/main/groovy/ui/DefaultImportsGroovyShellTest.groovy =================================================================== --- src/main/groovy/ui/DefaultImportsGroovyShellTest.groovy (revision 0) +++ src/main/groovy/ui/DefaultImportsGroovyShellTest.groovy (revision 0) @@ -0,0 +1,104 @@ +package groovy.ui + +import junit.framework.TestCase + +/** + * Tests for DefaultImportsGroovyShell + * + * @author Sergii Bondarenko + * + * Created on 2009-02-01 + */ +public class DefaultImportsGroovyShellTest extends TestCase { + + /** + * Checks that import of classes classes from full script is applied automatically to selected part of script + */ + public void testDefaultImport() { + assert java.text.SimpleDateFormat == runSelected('import java.text.SimpleDateFormat', 'return SimpleDateFormat') + } + + /** + * Checks that import of classes with aliases from full script is applied automatically to selected part of script + */ + public void testDefaultImportWithAlias() { + assert java.text.SimpleDateFormat == runSelected('import java.text.SimpleDateFormat as SF', 'return SF') + } + + /** + * Checks that import of packages from full script is applied automatically to selected part of script + */ + public void testDefaultImportPackage() { + assert java.text.SimpleDateFormat == runSelected('import java.text.*', 'return SimpleDateFormat') + } + + /** + * Checks that static import of class from full script is applied automatically to selected part of script + */ + public void testDefaultStaticImportClass() { + assert Long.MAX_VALUE == runSelected('import static java.lang.Long.*', 'MAX_VALUE') + } + + /** + * Checks that static import of method from full script is applied automatically to selected part of script + */ + public void testDefaultStaticImportMethod() { + assert '12345' == runSelected('import static java.lang.String.valueOf;', 'valueOf(12345)') + } + + /** + * Checks that static import of field from full script is applied automatically to selected part of script + */ + public void testDefaultStaticImportField() { + assert Integer.MAX_VALUE == runSelected('import static java.lang.Integer.MAX_VALUE', 'MAX_VALUE') + } + + /** + * Checks that errors in whole script are ignored, when only selected part of script is executed + */ + public void testErrorsInWholeScript() { + assert 1 == runSelected('aa#@!#@$', 'return 1') + } + + /** + * Checks that class imports of selected part of script have higher priority than imports of whole script + */ + public void testHigherClassImportsPriorityOfSelection() { + assert java.lang.Math == runSelected('import java.text.SimpleDateFormat', 'import java.lang.Math as SimpleDateFormat; SimpleDateFormat') + } + + /** + * Checks that package imports of selected part of script have higher priority than imports of whole script + */ + public void testHigherPackageImportsPriorityOfSelection() { + assert java.lang.Math == runSelected('import java.text.SimpleDateFormat', 'import java.lang.Math as SimpleDateFormat; SimpleDateFormat') + } + + /** + * Checks that static class imports of selected part of script have higher priority than imports of whole script + */ + public void testHigherStaticClassImportsPriorityOfSelection() { + assert Long.MAX_VALUE == runSelected('import static java.lang.Integer.*', 'import static java.lang.Long.*; MAX_VALUE') + } + + /** + * Checks that static method imports of selected part of script have higher priority than imports of whole script + */ + public void testHigherStaticMethodImportsPriorityOfSelection() { + assert 123 == runSelected('import static java.lang.String.valueOf', 'import static java.lang.Integer.valueOf; valueOf(123)') + } + + /** + * Checks that static field imports of selected part of script have higher priority than imports of whole script + */ + public void testHigherStaticFieldImportsPriorityOfSelection() { + assert Long.MAX_VALUE == runSelected('import static java.lang.Integer.MAX_VALUE', 'import static java.lang.Long.MAX_VALUE; MAX_VALUE') + } + + /** + * Helping method for running part of script in context of whole script + */ + private def runSelected(fullText, selectedText) { + return new DefaultImportsGroovyShell().runSelected(fullText, selectedText, "SelectedScript", null) + } +} \ No newline at end of file