This is an automated email from the ASF dual-hosted git repository.
paulk pushed a commit to branch jline3
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/jline3 by this push:
new cce392c1d0 support records and interfaces amd static imports
cce392c1d0 is described below
commit cce392c1d02d291e2cb686011a6b75ea9a15f840
Author: Paul King <[email protected]>
AuthorDate: Thu Jul 3 20:25:58 2025 +1000
support records and interfaces amd static imports
---
.../apache/groovy/groovysh/jline/GroovyEngine.java | 69 ++++++++++++++++++----
.../groovysh/commands/ImportCommandTest.groovy | 50 ----------------
.../groovy/groovysh/commands/ImportTest.groovy | 30 ++++++++++
3 files changed, 88 insertions(+), 61 deletions(-)
diff --git
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyEngine.java
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyEngine.java
index c4c1bfb3e8..895d5085d9 100644
---
a/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyEngine.java
+++
b/subprojects/groovy-groovysh/src/main/groovy/org/apache/groovy/groovysh/jline/GroovyEngine.java
@@ -106,10 +106,16 @@ public class GroovyEngine implements ScriptEngine {
private static final String REGEX_VAR = "[a-zA-Z_]+[a-zA-Z0-9_]*";
private static final Pattern PATTERN_FUNCTION_DEF = Pattern.compile(
"^def\\s+(" + REGEX_VAR + ")\\s*\\(([a-zA-Z0-9_
,]*)\\)\\s*\\{(.*)?}(|\n)$", Pattern.DOTALL);
+ private static final Pattern PATTERN_CLASSLIKE_DEF =
+ Pattern.compile("^(class|record|interface|trait)\\s+(" + REGEX_VAR
+ ")\\s*(.*?\\{.*?})(|\n)$", Pattern.DOTALL);
private static final Pattern PATTERN_CLASS_DEF =
Pattern.compile("^class\\s+(" + REGEX_VAR +
")\\s*(.*?\\{.*?})(|\n)$", Pattern.DOTALL);
private static final Pattern PATTERN_TRAIT_DEF =
- Pattern.compile("^trait\\s+(" + REGEX_VAR +
")\\s*(\\{.*?})(|\n)$", Pattern.DOTALL);
+ Pattern.compile("^trait\\s+(" + REGEX_VAR +
")\\s*(.*?\\{.*?})(|\n)$", Pattern.DOTALL);
+ private static final Pattern PATTERN_INTERFACE_DEF =
+ Pattern.compile("^interface\\s+(" + REGEX_VAR +
")\\s*(.*?\\{.*?})(|\n)$", Pattern.DOTALL);
+ private static final Pattern PATTERN_RECORD_DEF =
+ Pattern.compile("^record\\s+(" + REGEX_VAR +
")\\s*(.*?\\{.*?})(|\n)$", Pattern.DOTALL);
private static final String REGEX_CLASS = "(.*?)\\.([A-Z_].*)";
private static final Pattern PATTERN_CLASS = Pattern.compile(REGEX_CLASS);
private static final String REGEX_PACKAGE = "([a-z][a-z_0-9]*\\.)*";
@@ -121,6 +127,7 @@ public class GroovyEngine implements ScriptEngine {
"java.util.*",
"java.io.*",
"java.net.*",
+ "java.time.*",
"groovy.lang.*",
"groovy.util.*",
"java.math.BigInteger",
@@ -131,7 +138,9 @@ public class GroovyEngine implements ScriptEngine {
private final Map<String, String> imports = new HashMap<>();
private final Map<String, String> methods = new HashMap<>();
private final Map<String, String> classes = new HashMap<>();
+ private final Map<String, String> interfaces = new HashMap<>();
private final Map<String, String> traits = new HashMap<>();
+ private final Map<String, String> records = new HashMap<>();
private final Map<String, Class<?>> nameClass;
private Cloner objectCloner = new ObjectCloner();
protected final EngineClassLoader classLoader;
@@ -334,23 +343,37 @@ public class GroovyEngine implements ScriptEngine {
@Override
public Object execute(String statement) throws Exception {
Object out = null;
- if (statement.matches("import\\s+(([^;\\s])+)\\s*(;)?")) {
+ if (statement.matches("import\\s+(static\\s+)?(([^;\\s])+)\\s*(;)?")) {
String[] p = statement.split("\\s+");
- String classname = p[1].replaceAll(";", "");
+ int classIdx = 1;
+ boolean isStatic = p[1].equals("static");
+ if (isStatic) {
+ classIdx++;
+ }
+ String classname = p[classIdx].replaceAll(";", "");
+ addToNameClass(classname);
+ if (isStatic) {
+ classname = "static " + classname;
+ }
executeStatement(shell, imports, statement);
imports.put(classname, statement);
- addToNameClass(classname);
} else if (statement.equals("import")) {
- out = new ArrayList<>(imports.keySet());
+ List<String> allImports = new ArrayList<>(imports.keySet());
+ allImports.addAll(DEFAULT_IMPORTS);
+ out = allImports;
} else if (functionDef(statement)) {
// do nothing
} else if (statement.equals("def")) {
out = methods;
} else if (statement.equals("class")) {
out = classes;
- } else if (statement.equals("traits")) {
+ } else if (statement.equals("record")) {
+ out = records;
+ } else if (statement.equals("trait")) {
out = traits;
- } else if (statement.matches("(def|class|trait)\\s+" + REGEX_VAR)) {
+ } else if (statement.equals("interface")) {
+ out = interfaces;
+ } else if (statement.matches("(def|class|trait|interface|record)\\s+"
+ REGEX_VAR)) {
String name = statement.split("\\s+")[1];
if (statement.startsWith("def") && methods.containsKey(name)) {
out = "def " + name + methods.get(name);
@@ -358,6 +381,10 @@ public class GroovyEngine implements ScriptEngine {
out = "class " + name + " " + classes.get(name);
} else if (statement.startsWith("trait") &&
traits.containsKey(name)) {
out = "trait " + name + " " + traits.get(name);
+ } else if (statement.startsWith("record") &&
records.containsKey(name)) {
+ out = "record " + name + " " + records.get(name);
+ } else if (statement.startsWith("interface") &&
interfaces.containsKey(name)) {
+ out = "interface " + name + " " + interfaces.get(name);
}
} else {
out = executeStatement(shell, imports, statement);
@@ -371,6 +398,17 @@ public class GroovyEngine implements ScriptEngine {
Matcher matcher = PATTERN_TRAIT_DEF.matcher(statement);
matcher.matches();
traits.put(matcher.group(1), matcher.group(2));
+ addToNameClass(matcher.group(1));
+ } else if (PATTERN_INTERFACE_DEF.matcher(statement).matches()) {
+ Matcher matcher = PATTERN_INTERFACE_DEF.matcher(statement);
+ matcher.matches();
+ interfaces.put(matcher.group(1), matcher.group(2));
+ addToNameClass(matcher.group(1));
+ } else if (PATTERN_RECORD_DEF.matcher(statement).matches()) {
+ Matcher matcher = PATTERN_RECORD_DEF.matcher(statement);
+ matcher.matches();
+ records.put(matcher.group(1), matcher.group(2));
+ addToNameClass(matcher.group(1));
}
}
return out;
@@ -415,7 +453,7 @@ public class GroovyEngine implements ScriptEngine {
e.append(entry.getValue()).append("\n");
}
e.append(statement);
- if (classOrTraitDef(statement)) {
+ if (classLikeDef(statement)) {
e.append("; null");
}
return shell.evaluate(e.toString());
@@ -468,9 +506,8 @@ public class GroovyEngine implements ScriptEngine {
return out;
}
- private static boolean classOrTraitDef(String statement) {
- return PATTERN_CLASS_DEF.matcher(statement).matches()
- || PATTERN_TRAIT_DEF.matcher(statement).matches();
+ private static boolean classLikeDef(String statement) {
+ return PATTERN_CLASSLIKE_DEF.matcher(statement).matches();
}
private void refreshNameClass() {
@@ -502,6 +539,12 @@ public class GroovyEngine implements ScriptEngine {
} else if (classes.containsKey(var)) {
classes.remove(var);
classLoader.purgeClassCache(var + "(\\$.*)?");
+ } else if (interfaces.containsKey(var)) {
+ interfaces.remove(var);
+ classLoader.purgeClassCache(var + "(\\$.*)?");
+ } else if (records.containsKey(var)) {
+ records.remove(var);
+ classLoader.purgeClassCache(var + "(\\$.*)?");
} else if (traits.containsKey(var)) {
traits.remove(var);
classLoader.purgeClassCache(var + "(\\$.*)?");
@@ -607,6 +650,10 @@ public class GroovyEngine implements ScriptEngine {
new StringsCompleter("class"), new
StringsCompleter(classes::keySet), NullCompleter.INSTANCE));
completers.add(new ArgumentCompleter(
new StringsCompleter("trait"), new
StringsCompleter(traits::keySet), NullCompleter.INSTANCE));
+ completers.add(new ArgumentCompleter(
+ new StringsCompleter("interface"), new
StringsCompleter(interfaces::keySet), NullCompleter.INSTANCE));
+ completers.add(new ArgumentCompleter(
+ new StringsCompleter("record"), new
StringsCompleter(records::keySet), NullCompleter.INSTANCE));
completers.add(new ArgumentCompleter(
new StringsCompleter("import"),
new PackageCompleter(CandidateType.PACKAGE, this),
diff --git
a/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/ImportCommandTest.groovy
b/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/ImportCommandTest.groovy
deleted file mode 100644
index 10145a8455..0000000000
---
a/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/ImportCommandTest.groovy
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.groovy.groovysh.commands
-
-/**
- * Tests for the {@link ImportCommand} class.
- */
-class ImportCommandTest extends CommandTestSupport {
- void testPatternClassOrMethodName() {
- assert 'java.util.*'.matches(ImportCommand.IMPORTED_ITEM_PATTERN)
- assert 'java.util.Pattern'.matches(ImportCommand.IMPORTED_ITEM_PATTERN)
- assert 'static
java.util.Pattern.match'.matches(ImportCommand.IMPORTED_ITEM_PATTERN)
- assert 'java.util.Pattern as
Fattern'.matches(ImportCommand.IMPORTED_ITEM_PATTERN)
- assert 'java.util.Pattern'.matches(ImportCommand.IMPORTED_ITEM_PATTERN)
- assert
'java.util.P_attern'.matches(ImportCommand.IMPORTED_ITEM_PATTERN)
- }
-
- void testImport() {
- assert null == shell << 'import'
- assert 'java.awt.TextField' == shell << 'import java.awt.TextField'
- // test semicolon does not lead to duplicate import
- assert 'java.awt.TextField' == shell << 'import java.awt.TextField;'
- // test last import is added at the end
- assert 'java.awt.TextField, java.awt.TextArea' == shell << 'import
java.awt.TextArea;'
- assert 'java.awt.TextArea, java.awt.TextField' == shell << 'import
java.awt.TextField;'
- // test multiple commands are not allowed (as they would be executed
on every next buffer evaluation)
- assert null == shell << 'import java.awt.TextField; println("foo")'
- // test *, recognizing unnecessary imports sadly not implemented
- assert 'java.awt.TextArea, java.awt.TextField, java.awt.*' == shell <<
'import java.awt.*'
- // test numerics being allowed in class/package names
- assert 'java.awt.TextArea, java.awt.TextField, java.awt.*,
org.w3c.dom.*' == shell << 'import org.w3c.dom.*'
- assert 'java.awt.TextArea, java.awt.TextField, java.awt.*,
org.w3c.dom.*, java.awt.Graphics2D' == shell << 'import java.awt.Graphics2D'
- }
-}
diff --git
a/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/ImportTest.groovy
b/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/ImportTest.groovy
new file mode 100644
index 0000000000..efb8d4dbb8
--- /dev/null
+++
b/subprojects/groovy-groovysh/src/test/groovy/org/apache/groovy/groovysh/commands/ImportTest.groovy
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.groovy.groovysh.commands
+
+/**
+ * Tests for the {@code import} command.
+ */
+class ImportTest extends SystemTestSupport {
+ void testImport() {
+ assert system.execute('import') == []
+ system.execute('import java.awt.TextField')
+ assert system.execute('import') == ['java.awt.TextField']
+ }
+}