You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/20 00:56:35 UTC
[38/49] groovy git commit: Move source files to proper packages
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovyShell.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovyShell.java b/src/main/groovy/groovy/lang/GroovyShell.java
new file mode 100644
index 0000000..4dc51c9
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovyShell.java
@@ -0,0 +1,611 @@
+/*
+ * 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 groovy.lang;
+
+import groovy.security.GroovyCodeSourcePermission;
+import groovy.ui.GroovyMain;
+import org.apache.groovy.plugin.GroovyRunner;
+import org.apache.groovy.plugin.GroovyRunnerRegistry;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.List;
+
+import static org.codehaus.groovy.runtime.InvokerHelper.MAIN_METHOD_NAME;
+
+/**
+ * Represents a groovy shell capable of running arbitrary groovy scripts
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ * @author Paul King
+ */
+public class GroovyShell extends GroovyObjectSupport {
+
+ public static final String DEFAULT_CODE_BASE = "/groovy/shell";
+
+ private final Binding context;
+ private int counter;
+ private final CompilerConfiguration config;
+ private GroovyClassLoader loader;
+
+ public static void main(String[] args) {
+ GroovyMain.main(args);
+ }
+
+ public GroovyShell() {
+ this(null, new Binding());
+ }
+
+ public GroovyShell(Binding binding) {
+ this(null, binding);
+ }
+
+ public GroovyShell(ClassLoader parent, CompilerConfiguration config) {
+ this(parent, new Binding(), config);
+ }
+
+ public GroovyShell(CompilerConfiguration config) {
+ this(new Binding(), config);
+ }
+
+ public GroovyShell(Binding binding, CompilerConfiguration config) {
+ this(null, binding, config);
+ }
+
+ public GroovyShell(ClassLoader parent, Binding binding) {
+ this(parent, binding, CompilerConfiguration.DEFAULT);
+ }
+
+ public GroovyShell(ClassLoader parent) {
+ this(parent, new Binding(), CompilerConfiguration.DEFAULT);
+ }
+
+ public GroovyShell(ClassLoader parent, Binding binding, final CompilerConfiguration config) {
+ if (binding == null) {
+ throw new IllegalArgumentException("Binding must not be null.");
+ }
+ if (config == null) {
+ throw new IllegalArgumentException("Compiler configuration must not be null.");
+ }
+ final ClassLoader parentLoader = (parent!=null)?parent:GroovyShell.class.getClassLoader();
+ this.loader = AccessController.doPrivileged(new PrivilegedAction<GroovyClassLoader>() {
+ public GroovyClassLoader run() {
+ return new GroovyClassLoader(parentLoader,config);
+ }
+ });
+ this.context = binding;
+ this.config = config;
+ }
+
+ public void resetLoadedClasses() {
+ loader.clearCache();
+ }
+
+ /**
+ * Creates a child shell using a new ClassLoader which uses the parent shell's
+ * class loader as its parent
+ *
+ * @param shell is the parent shell used for the variable bindings and the parent class loader
+ */
+ public GroovyShell(GroovyShell shell) {
+ this(shell.loader, shell.context);
+ }
+
+ public Binding getContext() {
+ return context;
+ }
+
+ public GroovyClassLoader getClassLoader() {
+ return loader;
+ }
+
+ public Object getProperty(String property) {
+ Object answer = getVariable(property);
+ if (answer == null) {
+ answer = super.getProperty(property);
+ }
+ return answer;
+ }
+
+ public void setProperty(String property, Object newValue) {
+ setVariable(property, newValue);
+ try {
+ super.setProperty(property, newValue);
+ } catch (GroovyRuntimeException e) {
+ // ignore, was probably a dynamic property
+ }
+ }
+
+ //
+ // FIXME: Use List<String> here, current version is not safe
+ //
+
+ /**
+ * A helper method which runs the given script file with the given command line arguments
+ *
+ * @param scriptFile the file of the script to run
+ * @param list the command line arguments to pass in
+ */
+ public Object run(File scriptFile, List list) throws CompilationFailedException, IOException {
+ String[] args = new String[list.size()];
+ return run(scriptFile, (String[]) list.toArray(args));
+ }
+
+ /**
+ * A helper method which runs the given cl script with the given command line arguments
+ *
+ * @param scriptText is the text content of the script
+ * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+ * @param list the command line arguments to pass in
+ */
+ public Object run(String scriptText, String fileName, List list) throws CompilationFailedException {
+ String[] args = new String[list.size()];
+ list.toArray(args);
+ return run(scriptText, fileName, args);
+ }
+
+ /**
+ * Runs the given script file name with the given command line arguments
+ *
+ * @param scriptFile the file name of the script to run
+ * @param args the command line arguments to pass in
+ */
+ public Object run(final File scriptFile, String[] args) throws CompilationFailedException, IOException {
+ String scriptName = scriptFile.getName();
+ int p = scriptName.lastIndexOf(".");
+ if (p++ >= 0) {
+ if (scriptName.substring(p).equals("java")) {
+ throw new CompilationFailedException(0, null);
+ }
+ }
+
+ // Get the current context classloader and save it on the stack
+ final Thread thread = Thread.currentThread();
+ //ClassLoader currentClassLoader = thread.getContextClassLoader();
+
+ class DoSetContext implements PrivilegedAction {
+ ClassLoader classLoader;
+
+ public DoSetContext(ClassLoader loader) {
+ classLoader = loader;
+ }
+
+ public Object run() {
+ thread.setContextClassLoader(classLoader);
+ return null;
+ }
+ }
+
+ AccessController.doPrivileged(new DoSetContext(loader));
+
+ // Parse the script, generate the class, and invoke the main method. This is a little looser than
+ // if you are compiling the script because the JVM isn't executing the main method.
+ Class scriptClass;
+ try {
+ scriptClass = AccessController.doPrivileged(new PrivilegedExceptionAction<Class>() {
+ public Class run() throws CompilationFailedException, IOException {
+ return loader.parseClass(scriptFile);
+ }
+ });
+ } catch (PrivilegedActionException pae) {
+ Exception e = pae.getException();
+ if (e instanceof CompilationFailedException) {
+ throw (CompilationFailedException) e;
+ } else if (e instanceof IOException) {
+ throw (IOException) e;
+ } else {
+ throw (RuntimeException) pae.getException();
+ }
+ }
+
+ return runScriptOrMainOrTestOrRunnable(scriptClass, args);
+
+ // Set the context classloader back to what it was.
+ //AccessController.doPrivileged(new DoSetContext(currentClassLoader));
+ }
+
+ /**
+ * if (theClass is a Script) {
+ * run it like a script
+ * } else if (theClass has a main method) {
+ * run the main method
+ * } else if (theClass instanceof GroovyTestCase) {
+ * use the test runner to run it
+ * } else if (theClass implements Runnable) {
+ * if (theClass has a constructor with String[] params)
+ * instantiate theClass with this constructor and run
+ * else if (theClass has a no-args constructor)
+ * instantiate theClass with the no-args constructor and run
+ * }
+ */
+ private Object runScriptOrMainOrTestOrRunnable(Class scriptClass, String[] args) {
+ // Always set the "args" property, regardless of what path we take in the code.
+ // Bad enough to have side effects but worse if their behavior is wonky.
+ context.setProperty("args", args);
+
+ if (scriptClass == null) {
+ return null;
+ }
+
+ //TODO: This logic mostly duplicates InvokerHelper.createScript. They should probably be unified.
+
+ if (Script.class.isAssignableFrom(scriptClass)) {
+ // treat it just like a script if it is one
+ try {
+ Script script = InvokerHelper.newScript(scriptClass, context);
+ return script.run();
+ } catch (InstantiationException e) {
+ // ignore instantiation errors,, try to do main
+ } catch (IllegalAccessException e) {
+ // ignore instantiation errors, try to do main
+ } catch (InvocationTargetException e) {
+ // ignore instantiation errors, try to do main
+ }
+ }
+ try {
+ // let's find a main method
+ scriptClass.getMethod(MAIN_METHOD_NAME, String[].class);
+ // if that main method exist, invoke it
+ return InvokerHelper.invokeMethod(scriptClass, MAIN_METHOD_NAME, new Object[]{args});
+ } catch (NoSuchMethodException e) {
+ // if it implements Runnable, try to instantiate it
+ if (Runnable.class.isAssignableFrom(scriptClass)) {
+ return runRunnable(scriptClass, args);
+ }
+ GroovyRunnerRegistry runnerRegistry = GroovyRunnerRegistry.getInstance();
+ for (GroovyRunner runner : runnerRegistry) {
+ if (runner.canRun(scriptClass, this.loader)) {
+ return runner.run(scriptClass, this.loader);
+ }
+ }
+ StringBuilder message = new StringBuilder("This script or class could not be run.\n" +
+ "It should either:\n" +
+ "- have a main method,\n" +
+ "- be a JUnit test or extend GroovyTestCase,\n" +
+ "- implement the Runnable interface,\n" +
+ "- or be compatible with a registered script runner. Known runners:\n");
+ if (runnerRegistry.isEmpty()) {
+ message.append(" * <none>");
+ } else {
+ for (String key : runnerRegistry.keySet()) {
+ message.append(" * ").append(key).append("\n");
+ }
+ }
+ throw new GroovyRuntimeException(message.toString());
+ }
+ }
+
+ private static Object runRunnable(Class scriptClass, String[] args) {
+ Constructor constructor = null;
+ Runnable runnable = null;
+ Throwable reason = null;
+ try {
+ // first, fetch the constructor taking String[] as parameter
+ constructor = scriptClass.getConstructor((new String[]{}).getClass());
+ try {
+ // instantiate a runnable and run it
+ runnable = (Runnable) constructor.newInstance(new Object[]{args});
+ } catch (Throwable t) {
+ reason = t;
+ }
+ } catch (NoSuchMethodException e1) {
+ try {
+ // otherwise, find the default constructor
+ constructor = scriptClass.getConstructor();
+ try {
+ // instantiate a runnable and run it
+ runnable = (Runnable) constructor.newInstance();
+ } catch (InvocationTargetException ite) {
+ throw new InvokerInvocationException(ite.getTargetException());
+ } catch (Throwable t) {
+ reason = t;
+ }
+ } catch (NoSuchMethodException nsme) {
+ reason = nsme;
+ }
+ }
+ if (constructor != null && runnable != null) {
+ runnable.run();
+ } else {
+ throw new GroovyRuntimeException("This script or class was runnable but could not be run. ", reason);
+ }
+ return null;
+ }
+
+ /**
+ * Runs the given script text with command line arguments
+ *
+ * @param scriptText is the text content of the script
+ * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+ * @param args the command line arguments to pass in
+ */
+ public Object run(final String scriptText, final String fileName, String[] args) throws CompilationFailedException {
+ GroovyCodeSource gcs = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
+ public GroovyCodeSource run() {
+ return new GroovyCodeSource(scriptText, fileName, DEFAULT_CODE_BASE);
+ }
+ });
+ return run(gcs, args);
+ }
+
+ /**
+ * Runs the given script source with command line arguments
+ *
+ * @param source is the source content of the script
+ * @param args the command line arguments to pass in
+ */
+ public Object run(GroovyCodeSource source, List args) throws CompilationFailedException {
+ return run(source, ((String[]) args.toArray(new String[args.size()])));
+ }
+
+ /**
+ * Runs the given script source with command line arguments
+ *
+ * @param source is the source content of the script
+ * @param args the command line arguments to pass in
+ */
+ public Object run(GroovyCodeSource source, String[] args) throws CompilationFailedException {
+ Class scriptClass = parseClass(source);
+ return runScriptOrMainOrTestOrRunnable(scriptClass, args);
+ }
+
+ /**
+ * Runs the given script source with command line arguments
+ *
+ * @param source is the source content of the script
+ * @param args the command line arguments to pass in
+ */
+ public Object run(URI source, List args) throws CompilationFailedException, IOException {
+ return run(new GroovyCodeSource(source), ((String[]) args.toArray(new String[args.size()])));
+ }
+
+ /**
+ * Runs the given script source with command line arguments
+ *
+ * @param source is the source content of the script
+ * @param args the command line arguments to pass in
+ */
+ public Object run(URI source, String[] args) throws CompilationFailedException, IOException {
+ return run(new GroovyCodeSource(source), args);
+ }
+
+ /**
+ * Runs the given script with command line arguments
+ *
+ * @param in the stream reading the script
+ * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+ * @param list the command line arguments to pass in
+ */
+ public Object run(final Reader in, final String fileName, List list) throws CompilationFailedException {
+ return run(in, fileName, (String[]) list.toArray(new String[list.size()]));
+ }
+
+ /**
+ * Runs the given script with command line arguments
+ *
+ * @param in the stream reading the script
+ * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+ * @param args the command line arguments to pass in
+ */
+ public Object run(final Reader in, final String fileName, String[] args) throws CompilationFailedException {
+ GroovyCodeSource gcs = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
+ public GroovyCodeSource run() {
+ return new GroovyCodeSource(in, fileName, DEFAULT_CODE_BASE);
+ }
+ });
+ Class scriptClass = parseClass(gcs);
+ return runScriptOrMainOrTestOrRunnable(scriptClass, args);
+ }
+
+ public Object getVariable(String name) {
+ return context.getVariables().get(name);
+ }
+
+ public void setVariable(String name, Object value) {
+ context.setVariable(name, value);
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result
+ *
+ * @param codeSource
+ * @throws CompilationFailedException
+ */
+ public Object evaluate(GroovyCodeSource codeSource) throws CompilationFailedException {
+ Script script = parse(codeSource);
+ return script.run();
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result
+ *
+ * @param scriptText the text of the script
+ */
+ public Object evaluate(final String scriptText) throws CompilationFailedException {
+ return evaluate(scriptText, generateScriptName(), DEFAULT_CODE_BASE);
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result
+ *
+ * @param scriptText the text of the script
+ * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+ */
+ public Object evaluate(String scriptText, String fileName) throws CompilationFailedException {
+ return evaluate(scriptText, fileName, DEFAULT_CODE_BASE);
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result.
+ * The .class file created from the script is given the supplied codeBase
+ */
+ public Object evaluate(final String scriptText, final String fileName, final String codeBase) throws CompilationFailedException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new GroovyCodeSourcePermission(codeBase));
+ }
+
+ GroovyCodeSource gcs = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
+ public GroovyCodeSource run() {
+ return new GroovyCodeSource(scriptText, fileName, codeBase);
+ }
+ });
+
+ return evaluate(gcs);
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result
+ *
+ * @param file is the file of the script (which is used to create the class name of the script)
+ */
+ public Object evaluate(File file) throws CompilationFailedException, IOException {
+ return evaluate(new GroovyCodeSource(file, config.getSourceEncoding()));
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result
+ *
+ * @param uri is the URI of the script (which is used to create the class name of the script)
+ */
+ public Object evaluate(URI uri) throws CompilationFailedException, IOException {
+ return evaluate(new GroovyCodeSource(uri));
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result
+ *
+ * @param in the stream reading the script
+ */
+ public Object evaluate(Reader in) throws CompilationFailedException {
+ return evaluate(in, generateScriptName());
+ }
+
+ /**
+ * Evaluates some script against the current Binding and returns the result
+ *
+ * @param in the stream reading the script
+ * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+ */
+ public Object evaluate(Reader in, String fileName) throws CompilationFailedException {
+ Script script = null;
+ try {
+ script = parse(in, fileName);
+ return script.run();
+ } finally {
+ if (script != null) {
+ InvokerHelper.removeClass(script.getClass());
+ }
+ }
+ }
+
+
+ /**
+ * Parses the given script and returns it ready to be run
+ *
+ * @param reader the stream reading the script
+ * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+ * @return the parsed script which is ready to be run via {@link Script#run()}
+ */
+ public Script parse(final Reader reader, final String fileName) throws CompilationFailedException {
+ return parse(new GroovyCodeSource(reader, fileName, DEFAULT_CODE_BASE));
+ }
+
+ /**
+ * Parses the groovy code contained in codeSource and returns a java class.
+ */
+ private Class parseClass(final GroovyCodeSource codeSource) throws CompilationFailedException {
+ // Don't cache scripts
+ return loader.parseClass(codeSource, false);
+ }
+
+ /**
+ * Parses the given script and returns it ready to be run. When running in a secure environment
+ * (-Djava.security.manager) codeSource.getCodeSource() determines what policy grants should be
+ * given to the script.
+ *
+ * @param codeSource
+ * @return ready to run script
+ */
+ public Script parse(final GroovyCodeSource codeSource) throws CompilationFailedException {
+ return InvokerHelper.createScript(parseClass(codeSource), context);
+ }
+
+ /**
+ * Parses the given script and returns it ready to be run
+ *
+ * @param file is the file of the script (which is used to create the class name of the script)
+ */
+ public Script parse(File file) throws CompilationFailedException, IOException {
+ return parse(new GroovyCodeSource(file, config.getSourceEncoding()));
+ }
+
+ /**
+ * Parses the given script and returns it ready to be run
+ *
+ * @param uri is the URI of the script (which is used to create the class name of the script)
+ */
+ public Script parse(URI uri) throws CompilationFailedException, IOException {
+ return parse(new GroovyCodeSource(uri));
+ }
+
+ /**
+ * Parses the given script and returns it ready to be run
+ *
+ * @param scriptText the text of the script
+ */
+ public Script parse(String scriptText) throws CompilationFailedException {
+ return parse(scriptText, generateScriptName());
+ }
+
+ public Script parse(final String scriptText, final String fileName) throws CompilationFailedException {
+ GroovyCodeSource gcs = AccessController.doPrivileged(new PrivilegedAction<GroovyCodeSource>() {
+ public GroovyCodeSource run() {
+ return new GroovyCodeSource(scriptText, fileName, DEFAULT_CODE_BASE);
+ }
+ });
+ return parse(gcs);
+ }
+
+ /**
+ * Parses the given script and returns it ready to be run
+ *
+ * @param in the stream reading the script
+ */
+ public Script parse(Reader in) throws CompilationFailedException {
+ return parse(in, generateScriptName());
+ }
+
+ protected synchronized String generateScriptName() {
+ return "Script" + (++counter) + ".groovy";
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/GroovySystem.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/GroovySystem.java b/src/main/groovy/groovy/lang/GroovySystem.java
new file mode 100644
index 0000000..cb5ea98
--- /dev/null
+++ b/src/main/groovy/groovy/lang/GroovySystem.java
@@ -0,0 +1,103 @@
+/*
+ * 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 groovy.lang;
+
+import org.apache.groovy.plugin.GroovyRunner;
+import org.apache.groovy.plugin.GroovyRunnerRegistry;
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
+import org.codehaus.groovy.util.ReferenceBundle;
+import org.codehaus.groovy.util.ReleaseInfo;
+
+import java.util.Map;
+
+public final class GroovySystem {
+ //
+ // TODO: make this initialization able to set useReflection true
+ // TODO: have some way of specifying another MetaClass Registry implementation
+ //
+ static {
+ USE_REFLECTION = true;
+ META_CLASS_REGISTRY = new MetaClassRegistryImpl();
+ }
+
+ /**
+ * If true then the MetaClass will only use reflection for method dispatch, property access, etc.
+ */
+ @Deprecated
+ private static final boolean USE_REFLECTION;
+
+ /**
+ * Reference to the MetaClass Registry to be used by the Groovy run-time system to map classes to MetaClasses
+ */
+ private static final MetaClassRegistry META_CLASS_REGISTRY;
+
+ /**
+ * Reference to the Runtime Registry to be used by the Groovy run-time system to find classes capable of running scripts
+ *
+ * @deprecated use {@link GroovyRunnerRegistry}
+ */
+ @Deprecated
+ public static final Map<String, GroovyRunner> RUNNER_REGISTRY = GroovyRunnerRegistry.getInstance();
+
+ private static boolean keepJavaMetaClasses=false;
+
+ private GroovySystem() {
+ // Do not allow this class to be instantiated
+ }
+
+ @Deprecated
+ public static boolean isUseReflection() {
+ return USE_REFLECTION;
+ }
+
+ public static MetaClassRegistry getMetaClassRegistry() {
+ return META_CLASS_REGISTRY;
+ }
+
+ public static void setKeepJavaMetaClasses(boolean keepJavaMetaClasses) {
+ GroovySystem.keepJavaMetaClasses = keepJavaMetaClasses;
+ }
+
+ public static boolean isKeepJavaMetaClasses() {
+ return keepJavaMetaClasses;
+ }
+
+ /**
+ * This method can be used to ensure that no threaded created
+ * by a reference manager will be active. This is useful if the Groovy
+ * runtime itself is loaded through a class loader which should be disposed
+ * off. Without calling this method and if a threaded reference manager is
+ * active the class loader cannot be unloaded!
+ *
+ * Per default no threaded manager will be used.
+ *
+ * @since 1.6
+ */
+ public static void stopThreadedReferenceManager() {
+ ReferenceBundle.getSoftBundle().getManager().stopThread();
+ ReferenceBundle.getWeakBundle().getManager().stopThread();
+ }
+
+ /**
+ * Returns the groovy version
+ */
+ public static String getVersion() {
+ return ReleaseInfo.getVersion();
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Groovydoc.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Groovydoc.java b/src/main/groovy/groovy/lang/Groovydoc.java
new file mode 100644
index 0000000..7327913
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Groovydoc.java
@@ -0,0 +1,40 @@
+/*
+ * 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 groovy.lang;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation to hold the groovydoc for the annotated element at runtime, we call it "Runtime Groovydoc".
+ * Runtime Groovydoc is a bit like Python's Documentation Strings and will be useful for IDE and developers who set a high value on documentations.
+ *
+ * The usage is very simple, just place @Groovydoc at the beginning of the content of groovydoc, then the new parser Parrot will attach the annotation Groovydoc automatically
+ *
+ * @since 3.0.0
+ */
+@Documented
+@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Groovydoc {
+ String value();
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/IllegalPropertyAccessException.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/IllegalPropertyAccessException.java b/src/main/groovy/groovy/lang/IllegalPropertyAccessException.java
new file mode 100644
index 0000000..8227e3f
--- /dev/null
+++ b/src/main/groovy/groovy/lang/IllegalPropertyAccessException.java
@@ -0,0 +1,49 @@
+/*
+ * 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 groovy.lang;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * An exception occurred if a dynamic property dispatch fails with a
+ * field not accessible.
+ *
+ * @author <a href="mailto:blackdrag@uni.de">Jochen Theodorou</a>
+ */
+public class IllegalPropertyAccessException extends MissingPropertyException {
+
+ private static String makeMessage(String propertyName, Class clazz, int modifiers, boolean isField) {
+ String access = "private";
+ if (Modifier.isProtected(modifiers)) access = "protected";
+ if (Modifier.isPublic(modifiers)) access = "public";
+ String propertyType = "property";
+ if (isField) propertyType = "field";
+ return "Can not access the "+access+" "+propertyType+" "+propertyName+" in class "+clazz.getName();
+ }
+
+ public IllegalPropertyAccessException(String propertyName, Class clazz, int modifiers) {
+ super(makeMessage(propertyName,clazz,modifiers,false),propertyName,clazz);
+ }
+
+ public IllegalPropertyAccessException(Field field, Class clazz) {
+ super(makeMessage(field.getName(),clazz,field.getModifiers(),true),field.getName(),clazz);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/IncorrectClosureArgumentsException.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/IncorrectClosureArgumentsException.java b/src/main/groovy/groovy/lang/IncorrectClosureArgumentsException.java
new file mode 100644
index 0000000..f5b23f5
--- /dev/null
+++ b/src/main/groovy/groovy/lang/IncorrectClosureArgumentsException.java
@@ -0,0 +1,60 @@
+/*
+ * 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 groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * An exception occurred when invoking a Closure with the wrong number and/or
+ * types of arguments
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class IncorrectClosureArgumentsException extends GroovyRuntimeException {
+
+ private final Closure closure;
+ private final Object arguments;
+ private final Class[] expected;
+
+ public IncorrectClosureArgumentsException(Closure closure, Object arguments, Class[] expected) {
+ super(
+ "Incorrect arguments to closure: "
+ + closure
+ + ". Expected: "
+ + InvokerHelper.toString(expected)
+ + ", actual: "
+ + InvokerHelper.toString(arguments));
+ this.closure = closure;
+ this.arguments = arguments;
+ this.expected = expected;
+ }
+
+ public Object getArguments() {
+ return arguments;
+ }
+
+ public Closure getClosure() {
+ return closure;
+ }
+
+ public Class[] getExpected() {
+ return expected;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/IntRange.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/IntRange.java b/src/main/groovy/groovy/lang/IntRange.java
new file mode 100644
index 0000000..9377098
--- /dev/null
+++ b/src/main/groovy/groovy/lang/IntRange.java
@@ -0,0 +1,440 @@
+/*
+ * 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 groovy.lang;
+
+import org.codehaus.groovy.runtime.IteratorClosureAdapter;
+import org.codehaus.groovy.runtime.RangeInfo;
+
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Represents a list of Integer objects starting at a specified {@code from} value up (or down)
+ * to and potentially including a given {@code to} value.
+ * <p>
+ * Instances of this class may be either inclusive aware or non-inclusive aware. See the
+ * relevant constructors for creating each type. Inclusive aware IntRange instances are
+ * suitable for use with Groovy's range indexing - in particular if the from or to values
+ * might be negative. This normally happens underneath the covers but is worth keeping
+ * in mind if creating these ranges yourself explicitly.
+ * <p>
+ * Note: the design of this class might seem a little strange at first. It contains a Boolean
+ * field, {@code inclusive}, which can be {@code true}, {@code false} or {@code null}. This
+ * design is for backwards compatibility reasons. Groovy uses this class under the covers
+ * to represent range indexing, e.g. {@code someList[x..y]} and {@code someString[x..<y]}.
+ * In early versions of Groovy the ranges in these expressions were represented under the
+ * covers by the {@code new IntRange(x, y)} and {@code new IntRange(x, y-1)}. This turns
+ * out to be a lossy abstraction when x and/or y are negative values. Now the latter case
+ * is represented by {@code new IntRange(false, x, y)}.
+ * <p>
+ * Note: This class is a copy of {@link ObjectRange} optimized for <code>int</code>. If you make any
+ * changes to this class, you might consider making parallel changes to {@link ObjectRange}.
+ */
+public class IntRange extends AbstractList<Integer> implements Range<Integer> {
+
+ /**
+ * Iterates through each number in an <code>IntRange</code>.
+ */
+ private class IntRangeIterator implements Iterator<Integer> {
+ /**
+ * Counts from 0 up to size - 1.
+ */
+ private int index;
+
+ /**
+ * The number of values in the range.
+ */
+ private int size = size();
+
+ /**
+ * The next value to return.
+ */
+ private int value = isReverse() ? getTo() : getFrom();
+
+ @Override
+ public boolean hasNext() {
+ return index < size;
+ }
+
+ @Override
+ public Integer next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ if (index++ > 0) {
+ if (isReverse()) {
+ --value;
+ } else {
+ ++value;
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Not supported.
+ *
+ * @throws java.lang.UnsupportedOperationException always
+ */
+ @Override
+ public void remove() {
+ IntRange.this.remove(index);
+ }
+ }
+
+ /**
+ * For non-inclusive aware ranges, the first number in the range; <code>from</code> is always less than or equal to <code>to</code>.
+ * For inclusive aware ranges, the <code>from</code> argument supplied to the constructor.
+ */
+ private final int from;
+
+ /**
+ * For non-inclusive aware ranges, the last number in the range; <code>to</code> is always greater than or equal to <code>from</code>.
+ * For inclusive aware ranges, the <code>from</code> argument supplied to the constructor.
+ */
+ private final int to;
+
+ /**
+ * If <code>false</code>, counts up from <code>from</code> to <code>to</code>. Otherwise, counts down
+ * from <code>to</code> to <code>from</code>. Not used for inclusive-aware ranges (inclusive = true|false).
+ */
+ private final boolean reverse;
+
+ /**
+ * If <code>true</code> or null, <code>to</code> is included in the range.
+ * If <code>false</code>, the range stops before the <code>to</code> value.
+ * <p>
+ * Null for non-inclusive-aware ranges (which are inclusive).
+ * <p>
+ * If true or false, the reverse flag is discarded.
+ */
+ private final Boolean inclusive;
+
+ /**
+ * Creates a new non-inclusive aware <code>IntRange</code>. If <code>from</code> is greater than
+ * <code>to</code>, a reverse range is created with <code>from</code> and <code>to</code> swapped.
+ *
+ * @param from the first number in the range.
+ * @param to the last number in the range.
+ * @throws IllegalArgumentException if the range would contain more than {@link Integer#MAX_VALUE} values.
+ */
+ public IntRange(int from, int to) {
+ this.inclusive = null;
+ if (from > to) {
+ this.from = to;
+ this.to = from;
+ this.reverse = true;
+ } else {
+ this.from = from;
+ this.to = to;
+ this.reverse = false;
+ }
+ checkSize();
+ }
+
+ /**
+ * Creates a new non-inclusive aware <code>IntRange</code>.
+ *
+ * @param from the first value in the range.
+ * @param to the last value in the range.
+ * @param reverse <code>true</code> if the range should count from
+ * <code>to</code> to <code>from</code>.
+ * @throws IllegalArgumentException if <code>from</code> is greater than <code>to</code>.
+ */
+ protected IntRange(int from, int to, boolean reverse) {
+ this.inclusive = null;
+ if (from > to) {
+ throw new IllegalArgumentException("'from' must be less than or equal to 'to'");
+ }
+
+ this.from = from;
+ this.to = to;
+ this.reverse = reverse;
+ checkSize();
+ }
+
+ /**
+ * Creates a new inclusive aware <code>IntRange</code>.
+ *
+ * @param from the first value in the range.
+ * @param to the last value in the range.
+ * @param inclusive <code>true</code> if the to value is included in the range.
+ */
+ public IntRange(boolean inclusive, int from, int to) {
+ this.from = from;
+ this.to = to;
+ this.inclusive = inclusive;
+ this.reverse = false; // range may still be reversed, this value is ignored for inclusive-aware ranges
+ checkSize();
+ }
+
+ /**
+ * Creates a new NumberRange with the same <code>from</code> and <code>to</code> as this
+ * IntRange but with a step size of <code>stepSize</code>.
+ *
+ * @param stepSize the desired step size
+ * @return a new NumberRange
+ * @since 2.5.0
+ */
+ public <T extends Number & Comparable> NumberRange by(T stepSize) {
+ return new NumberRange(NumberRange.comparableNumber((Number)from), NumberRange.comparableNumber((Number)to), stepSize, inclusive);
+ }
+
+ private void checkSize() {
+ // size() in the Collection interface returns an integer, so ranges can have no more than Integer.MAX_VALUE elements
+ final Long size = (long) to - from + 1;
+ if (size > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("A range must have no more than " + Integer.MAX_VALUE + " elements but attempted " + size + " elements");
+ }
+ }
+
+ /**
+ * A method for determining from and to information when using this IntRange to index an aggregate object of the specified size.
+ * Normally only used internally within Groovy but useful if adding range indexing support for your own aggregates.
+ *
+ * @param size the size of the aggregate being indexed
+ * @return the calculated range information (with 1 added to the to value, ready for providing to subList
+ */
+ public RangeInfo subListBorders(int size) {
+ if (inclusive == null) {
+ throw new IllegalStateException("Should not call subListBorders on a non-inclusive aware IntRange");
+ }
+ int tempFrom = from;
+ if (tempFrom < 0) {
+ tempFrom += size;
+ }
+ int tempTo = to;
+ if (tempTo < 0) {
+ tempTo += size;
+ }
+ if (tempFrom > tempTo) {
+ return new RangeInfo(inclusive ? tempTo : tempTo + 1, tempFrom + 1, true);
+ }
+ return new RangeInfo(tempFrom, inclusive ? tempTo + 1 : tempTo, false);
+ }
+
+ /**
+ * Determines if this object is equal to another object. Delegates to
+ * {@link AbstractList#equals(Object)} if <code>that</code> is anything
+ * other than an {@link IntRange}.
+ * <p>
+ * It is not necessary to override <code>hashCode</code>, as
+ * {@link AbstractList#hashCode()} provides a suitable hash code.<p>
+ * <p>
+ * Note that equals is generally handled by {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#equals(List, List)}
+ * instead of this method.
+ *
+ * @param that the object to compare
+ * @return <code>true</code> if the objects are equal
+ */
+ public boolean equals(Object that) {
+ return that instanceof IntRange ? equals((IntRange) that) : super.equals(that);
+ }
+
+ /**
+ * Compares an {@link IntRange} to another {@link IntRange}.
+ *
+ * @param that the object to compare for equality
+ * @return <code>true</code> if the ranges are equal
+ */
+ public boolean equals(IntRange that) {
+ return that != null && ((inclusive == null && reverse == that.reverse && from == that.from && to == that.to)
+ || (inclusive != null && inclusive == that.inclusive && from == that.from && to == that.to));
+ }
+
+ @Override
+ public Integer getFrom() {
+ if (inclusive == null || from <= to) {
+ return from;
+ }
+ return inclusive ? to : to + 1;
+ }
+
+ @Override
+ public Integer getTo() {
+ if (inclusive == null) {
+ return to;
+ }
+ if (from <= to) {
+ return inclusive ? to : to - 1;
+ }
+ return from;
+ }
+
+ /**
+ * Returns the inclusive flag. Null for non-inclusive aware ranges or non-null for inclusive aware ranges.
+ */
+ public Boolean getInclusive() {
+ return inclusive;
+ }
+
+ /**
+ * Gets the 'from' value as a primitive integer.
+ *
+ * @return the 'from' value as a primitive integer.
+ */
+ public int getFromInt() {
+ return getFrom();
+ }
+
+ /**
+ * Gets the 'to' value as a primitive integer.
+ *
+ * @return the 'to' value as a primitive integer.
+ */
+ public int getToInt() {
+ return getTo();
+ }
+
+ @Override
+ public boolean isReverse() {
+ return inclusive == null ? reverse : (from > to);
+ }
+
+ @Override
+ public boolean containsWithinBounds(Object o) {
+ return contains(o);
+ }
+
+ @Override
+ public Integer get(int index) {
+ if (index < 0) {
+ throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
+ }
+ if (index >= size()) {
+ throw new IndexOutOfBoundsException("Index: " + index + " too big for range: " + this);
+ }
+ return isReverse() ? getTo() - index : index + getFrom();
+ }
+
+ @Override
+ public int size() {
+ return getTo() - getFrom() + 1;
+ }
+
+ @Override
+ public Iterator<Integer> iterator() {
+ return new IntRangeIterator();
+ }
+
+ @Override
+ public List<Integer> subList(int fromIndex, int toIndex) {
+ if (fromIndex < 0) {
+ throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+ }
+ if (toIndex > size()) {
+ throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+ }
+ if (fromIndex > toIndex) {
+ throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+ }
+
+ if (fromIndex == toIndex) {
+ return new EmptyRange<Integer>(getFrom());
+ }
+
+ return new IntRange(fromIndex + getFrom(), toIndex + getFrom() - 1, isReverse());
+ }
+
+ public String toString() {
+ return inclusive != null ? ("" + from + ".." + (inclusive ? "" : "<") + to)
+ : (reverse ? "" + to + ".." + from : "" + from + ".." + to);
+ }
+
+ @Override
+ public String inspect() {
+ return toString();
+ }
+
+ @Override
+ public boolean contains(Object value) {
+ if (value instanceof Integer) {
+ return (Integer) value >= getFrom() && (Integer) value <= getTo();
+ }
+ if (value instanceof BigInteger) {
+ final BigInteger bigint = (BigInteger) value;
+ return bigint.compareTo(BigInteger.valueOf(getFrom())) >= 0 &&
+ bigint.compareTo(BigInteger.valueOf(getTo())) <= 0;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean containsAll(Collection other) {
+ if (other instanceof IntRange) {
+ final IntRange range = (IntRange) other;
+ return getFrom() <= range.getFrom() && range.getTo() <= getTo();
+ }
+ return super.containsAll(other);
+ }
+
+ @Override
+ public void step(int step, Closure closure) {
+ if (step == 0) {
+ if (!getFrom().equals(getTo())) {
+ throw new GroovyRuntimeException("Infinite loop detected due to step size of 0");
+ }
+ return; // from == to and step == 0, nothing to do, so return
+ }
+
+ if (isReverse()) {
+ step = -step;
+ }
+ if (step > 0) {
+ int value = getFrom();
+ while (value <= getTo()) {
+ closure.call(value);
+ if (((long) value + step) >= Integer.MAX_VALUE) {
+ break;
+ }
+ value = value + step;
+ }
+ } else {
+ int value = getTo();
+ while (value >= getFrom()) {
+ closure.call(value);
+ if (((long) value + step) <= Integer.MIN_VALUE) {
+ break;
+ }
+ value = value + step;
+ }
+ }
+ }
+
+ @Override
+ public List<Integer> step(int step) {
+ final IteratorClosureAdapter<Integer> adapter = new IteratorClosureAdapter<Integer>(this);
+ step(step, adapter);
+ return adapter.asList();
+ }
+
+ @Override
+ public int hashCode(){
+ int hashCode;
+ final int from = this.getFrom();
+ final int to = this.getTo();
+
+ hashCode = ((from+to+1)*(from+to))/2+to;
+ return hashCode;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Interceptor.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Interceptor.java b/src/main/groovy/groovy/lang/Interceptor.java
new file mode 100644
index 0000000..e496f1d
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Interceptor.java
@@ -0,0 +1,52 @@
+/*
+ * 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 groovy.lang;
+
+/**
+ * Implementers of this interface can be registered in the ProxyMetaClass for
+ * notifications about method calls for objects managed by the ProxyMetaClass.
+ * See groovy/lang/InterceptorTest.groovy for details.
+ * @author Dierk Koenig
+ */
+public interface Interceptor {
+ /**
+ * This code is executed before the method is optionally called.
+ * @param object receiver object for the method call
+ * @param methodName name of the method to call
+ * @param arguments arguments to the method call
+ * @return any arbitrary result that replaces the result of the
+ * original method call only if doInvoke() returns false and afterInvoke()
+ * relays this result.
+ */
+ Object beforeInvoke(Object object, String methodName, Object[] arguments);
+ /**
+ * This code is executed after the method is optionally called.
+ * @param object receiver object for the called method
+ * @param methodName name of the called method
+ * @param arguments arguments to the called method
+ * @param result result of the executed method call or result of beforeInvoke if method was not called
+ * @return any arbitrary result that can replace the result of the
+ * original method call. Typically, the result parameter is returned.
+ */
+ Object afterInvoke(Object object, String methodName, Object[] arguments, Object result);
+ /**
+ * @return whether the target method should be invoked at all.
+ */
+ boolean doInvoke();
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Lazy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/Lazy.java b/src/main/groovy/groovy/lang/Lazy.java
new file mode 100644
index 0000000..17f044f
--- /dev/null
+++ b/src/main/groovy/groovy/lang/Lazy.java
@@ -0,0 +1,156 @@
+/*
+ * 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 groovy.lang;
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Field annotation to simplify lazy initialization.
+ * <p>
+ * Example usage without any special modifiers just defers initialization until the first call but is not thread-safe:
+ * <pre>
+ * {@code @Lazy} T x
+ * </pre>
+ * becomes
+ * <pre>
+ * private T $x
+ *
+ * T getX() {
+ * if ($x != null)
+ * return $x
+ * else {
+ * $x = new T()
+ * return $x
+ * }
+ * }
+ * </pre>
+ *
+ * If the field is declared volatile then initialization will be synchronized using
+ * the <a href="http://en.wikipedia.org/wiki/Double-checked_locking">double-checked locking</a> pattern as shown here:
+ *
+ * <pre>
+ * {@code @Lazy} volatile T x
+ * </pre>
+ * becomes
+ * <pre>
+ * private volatile T $x
+ *
+ * T getX() {
+ * T $x_local = $x
+ * if ($x_local != null)
+ * return $x_local
+ * else {
+ * synchronized(this) {
+ * if ($x == null) {
+ * $x = new T()
+ * }
+ * return $x
+ * }
+ * }
+ * }
+ * </pre>
+ *
+ * By default a field will be initialized by calling its default constructor.
+ *
+ * If the field has an initial value expression then this expression will be used instead of calling the default constructor.
+ * In particular, it is possible to use closure <code>{ ... } ()</code> syntax as follows:
+ *
+ * <pre>
+ * {@code @Lazy} T x = { [1, 2, 3] } ()
+ * </pre>
+ * becomes
+ * <pre>
+ * private T $x
+ *
+ * T getX() {
+ * T $x_local = $x
+ * if ($x_local != null)
+ * return $x_local
+ * else {
+ * synchronized(this) {
+ * if ($x == null) {
+ * $x = { [1, 2, 3] } ()
+ * }
+ * return $x
+ * }
+ * }
+ * }
+ * </pre>
+ * <p>
+ * <code>@Lazy(soft=true)</code> will use a soft reference instead of the field and use the above rules each time re-initialization is required.
+ * <p>
+ * If the <code>soft</code> flag for the annotation is not set but the field is static, then
+ * the <a href="http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom">initialization on demand holder idiom</a> is
+ * used as follows:
+ * <pre>
+ * {@code @Lazy} static FieldType field
+ * {@code @Lazy} static Date date1
+ * {@code @Lazy} static Date date2 = { new Date().copyWith(year: 2000) }()
+ * {@code @Lazy} static Date date3 = new GregorianCalendar(2009, Calendar.JANUARY, 1).time
+ * </pre>
+ * becomes these methods and inners classes within the class containing the above definitions:
+ * <pre>
+ * private static class FieldTypeHolder_field {
+ * private static final FieldType INSTANCE = new FieldType()
+ * }
+ *
+ * private static class DateHolder_date1 {
+ * private static final Date INSTANCE = new Date()
+ * }
+ *
+ * private static class DateHolder_date2 {
+ * private static final Date INSTANCE = { new Date().copyWith(year: 2000) }()
+ * }
+ *
+ * private static class DateHolder_date3 {
+ * private static final Date INSTANCE = new GregorianCalendar(2009, Calendar.JANUARY, 1).time
+ * }
+ *
+ * static FieldType getField() {
+ * return FieldTypeHolder_field.INSTANCE
+ * }
+ *
+ * static Date getDate1() {
+ * return DateHolder_date1.INSTANCE
+ * }
+ *
+ * static Date getDate2() {
+ * return DateHolder_date2.INSTANCE
+ * }
+ *
+ * static Date getDate3() {
+ * return DateHolder_date3.INSTANCE
+ * }
+ * </pre>
+ */
+@java.lang.annotation.Documented
+@Retention(RetentionPolicy.SOURCE)
+@Target({ElementType.FIELD})
+@GroovyASTTransformationClass("org.codehaus.groovy.transform.LazyASTTransformation")
+public @interface Lazy {
+ /**
+ * @return if field should be soft referenced instead of hard referenced
+ */
+ boolean soft () default false;
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/ListWithDefault.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/ListWithDefault.java b/src/main/groovy/groovy/lang/ListWithDefault.java
new file mode 100644
index 0000000..6b87548
--- /dev/null
+++ b/src/main/groovy/groovy/lang/ListWithDefault.java
@@ -0,0 +1,257 @@
+/*
+ * 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 groovy.lang;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * A wrapper for {@link List} which automatically grows the list when either {@link #get(int)} or
+ * {@link #getAt(int)} is called with an index greater than or equal to {@code size()}.
+ *
+ * @author Andre Steingress
+ * @since 1.8.7
+ */
+public final class ListWithDefault<T> implements List<T> {
+
+ private final List<T> delegate;
+ private final boolean lazyDefaultValues;
+
+ private final Closure initClosure;
+
+ private ListWithDefault(List<T> items, boolean lazyDefaultValues, Closure initClosure) {
+ this.delegate = items;
+ this.lazyDefaultValues = lazyDefaultValues;
+ this.initClosure = initClosure;
+ }
+
+ public List<T> getDelegate() {
+ return delegate != null ? new ArrayList<T>(delegate) : null;
+ }
+
+ public boolean isLazyDefaultValues() {
+ return lazyDefaultValues;
+ }
+
+ public Closure getInitClosure() {
+ return initClosure != null ? (Closure) initClosure.clone() : null;
+ }
+
+ public static <T> ListWithDefault<T> newInstance(List<T> items, boolean lazyDefaultValues, Closure initClosure) {
+ if (items == null)
+ throw new IllegalArgumentException("Parameter \"items\" must not be null");
+ if (initClosure == null)
+ throw new IllegalArgumentException("Parameter \"initClosure\" must not be null");
+
+ return new ListWithDefault<T>(new ArrayList<T>(items), lazyDefaultValues, (Closure) initClosure.clone());
+ }
+
+ public int size() {
+ return delegate.size();
+ }
+
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ public boolean contains(Object o) {
+ return delegate.contains(o);
+ }
+
+ public Iterator<T> iterator() {
+ return delegate.iterator();
+ }
+
+ public Object[] toArray() {
+ return delegate.toArray();
+ }
+
+ public <T> T[] toArray(T[] ts) {
+ return delegate.toArray(ts);
+ }
+
+ public boolean add(T t) {
+ return delegate.add(t);
+ }
+
+ public boolean remove(Object o) {
+ return delegate.remove(o);
+ }
+
+ public boolean containsAll(Collection<?> objects) {
+ return delegate.containsAll(objects);
+ }
+
+ public boolean addAll(Collection<? extends T> ts) {
+ return delegate.addAll(ts);
+ }
+
+ public boolean addAll(int i, Collection<? extends T> ts) {
+ return delegate.addAll(i, ts);
+ }
+
+ public boolean removeAll(Collection<?> objects) {
+ return delegate.removeAll(objects);
+ }
+
+ public boolean retainAll(Collection<?> objects) {
+ return delegate.retainAll(objects);
+ }
+
+ public void clear() {
+ delegate.clear();
+ }
+
+ /**
+ * Overwrites subscript operator handling by redirecting to {@link #get(int)}.
+ *
+ * @param index an index (might be greater or equal to {@code size()}, or smaller than 0)
+ * @return the value at the given {@code index} or the default value
+ */
+ public T getAt(int index) {
+ return get(index);
+ }
+
+ /**
+ * Returns the element at the given index but grows the list if needed. If the requested {@code index} is
+ * greater than or equal to {@code size()}, the list will grow to the new size and a default value calculated
+ * using the <code>initClosure</code> will be used to populate the missing value and returned.
+ * <p>
+ * If <code>lazyDefaultValues</code> is <code>true</code> any gaps when growing the list are filled
+ * with nulls. Subsequent attempts to retrieve items from the list from those gap index values
+ * will, upon finding null, call the <code>initClosure</code> to populate the list for the
+ * given list value. Hence, when in this mode, nulls cannot be stored in this list.
+ * If <code>lazyDefaultValues</code> is <code>false</code> any gaps when growing the list are filled
+ * eagerly by calling the <code>initClosure</code> for all gap indexes during list growth.
+ * No calls to <code>initClosure</code> are made except during list growth and it is ok to
+ * store null values in the list when in this mode.
+ * <p>
+ * This implementation breaks
+ * the contract of {@link java.util.List#get(int)} as it a) possibly modifies the underlying list and b) does
+ * NOT throw an {@link IndexOutOfBoundsException} when {@code index < 0 || index >= size()}.
+ *
+ * @param index an index (might be greater or equal to {@code size()}, or smaller than 0)
+ * @return the value at the given {@code index} or the default value
+ */
+ public T get(int index) {
+
+ final int size = size();
+ int normalisedIndex = normaliseIndex(index, size);
+ if (normalisedIndex < 0) {
+ throw new IndexOutOfBoundsException("Negative index [" + normalisedIndex + "] too large for list size " + size);
+ }
+
+ // either index >= size or the normalised index is negative
+ if (normalisedIndex >= size) {
+ // find out the number of gaps to fill with null/the default value
+ final int gapCount = normalisedIndex - size;
+
+ // fill all gaps
+ for (int i = 0; i < gapCount; i++) {
+ final int idx = size();
+
+ // if we lazily create default values, use 'null' as placeholder
+ if (lazyDefaultValues)
+ delegate.add(idx, null);
+ else
+ delegate.add(idx, getDefaultValue(idx));
+ }
+
+ // add the first/last element being always the default value
+ final int idx = normalisedIndex;
+ delegate.add(idx, getDefaultValue(idx));
+
+ // normalise index again to get positive index
+ normalisedIndex = normaliseIndex(index, size());
+ }
+
+ T item = delegate.get(normalisedIndex);
+ if (item == null && lazyDefaultValues) {
+ item = getDefaultValue(normalisedIndex);
+ delegate.set(normalisedIndex, item);
+ }
+
+ return item;
+ }
+
+ @SuppressWarnings("unchecked")
+ private T getDefaultValue(int idx) {
+ return (T) initClosure.call(new Object[]{idx});
+ }
+
+ private static int normaliseIndex(int index, int size) {
+ if (index < 0) {
+ index += size;
+ }
+ return index;
+ }
+
+ public T set(int i, T t) {
+ return delegate.set(i, t);
+ }
+
+ public void add(int i, T t) {
+ delegate.add(i, t);
+ }
+
+ public T remove(int i) {
+ return delegate.remove(i);
+ }
+
+ public int indexOf(Object o) {
+ return delegate.indexOf(o);
+ }
+
+ public int lastIndexOf(Object o) {
+ return delegate.lastIndexOf(o);
+ }
+
+ public ListIterator<T> listIterator() {
+ return delegate.listIterator();
+ }
+
+ public ListIterator<T> listIterator(int i) {
+ return delegate.listIterator(i);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ /**
+ * Returns a view of a portion of this list. This method returns a list with the same
+ * lazy list settings as the original list.
+ *
+ * @param fromIndex low endpoint of the subList (inclusive)
+ * @param toIndex upper endpoint of the subList (exclusive)
+ * @return a view of a specified range within this list, keeping all lazy list settings
+ */
+ public ListWithDefault<T> subList(int fromIndex, int toIndex) {
+ return new ListWithDefault<T>(delegate.subList(fromIndex, toIndex), lazyDefaultValues, (Closure) initClosure.clone());
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MapWithDefault.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/MapWithDefault.java b/src/main/groovy/groovy/lang/MapWithDefault.java
new file mode 100644
index 0000000..166e06b
--- /dev/null
+++ b/src/main/groovy/groovy/lang/MapWithDefault.java
@@ -0,0 +1,105 @@
+/*
+ * 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 groovy.lang;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A wrapper for Map which allows a default value to be specified.
+ *
+ * @author Paul King
+ * @since 1.7.1
+ */
+public final class MapWithDefault<K, V> implements Map<K, V> {
+
+ private final Map<K, V> delegate;
+ private final Closure initClosure;
+
+ private MapWithDefault(Map<K, V> m, Closure initClosure) {
+ delegate = m;
+ this.initClosure = initClosure;
+ }
+
+ public static <K, V> Map<K, V> newInstance(Map<K, V> m, Closure initClosure) {
+ return new MapWithDefault<K, V>(m, initClosure);
+ }
+
+ public int size() {
+ return delegate.size();
+ }
+
+ public boolean isEmpty() {
+ return delegate.isEmpty();
+ }
+
+ public boolean containsKey(Object key) {
+ return delegate.containsKey(key);
+ }
+
+ public boolean containsValue(Object value) {
+ return delegate.containsValue(value);
+ }
+
+ public V get(Object key) {
+ if (!delegate.containsKey(key)) {
+ delegate.put((K)key, (V)initClosure.call(new Object[]{key}));
+ }
+ return delegate.get(key);
+ }
+
+ public V put(K key, V value) {
+ return delegate.put(key, value);
+ }
+
+ public V remove(Object key) {
+ return delegate.remove(key);
+ }
+
+ public void putAll(Map<? extends K, ? extends V> m) {
+ delegate.putAll(m);
+ }
+
+ public void clear() {
+ delegate.clear();
+ }
+
+ public Set<K> keySet() {
+ return delegate.keySet();
+ }
+
+ public Collection<V> values() {
+ return delegate.values();
+ }
+
+ public Set<Map.Entry<K, V>> entrySet() {
+ return delegate.entrySet();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return delegate.equals(obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaArrayLengthProperty.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/MetaArrayLengthProperty.java b/src/main/groovy/groovy/lang/MetaArrayLengthProperty.java
new file mode 100644
index 0000000..8310386
--- /dev/null
+++ b/src/main/groovy/groovy/lang/MetaArrayLengthProperty.java
@@ -0,0 +1,56 @@
+/*
+ * 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 groovy.lang;
+
+
+/**
+ * Represents the length property of an array
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class MetaArrayLengthProperty extends MetaProperty {
+
+ /**
+ * Sole constructor setting name to "length" and type to int
+ */
+ public MetaArrayLengthProperty() {
+ super("length", int.class);
+ }
+
+ /**
+ * Get this property from the given object.
+ * @param object an array
+ * @return the length of the array object
+ * @throws IllegalArgumentException if object is not an array
+ */
+ public Object getProperty(Object object) {
+ return java.lang.reflect.Array.getLength(object);
+ }
+
+ /**
+ * Sets the property on the given object to the new value
+ *
+ * @param object on which to set the property
+ * @param newValue the new value of the property
+ * @throws RuntimeException if the property could not be set
+ */
+ public void setProperty(Object object, Object newValue) {
+ throw new ReadOnlyPropertyException("length", object.getClass());
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/MetaBeanProperty.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/lang/MetaBeanProperty.java b/src/main/groovy/groovy/lang/MetaBeanProperty.java
new file mode 100644
index 0000000..89328af
--- /dev/null
+++ b/src/main/groovy/groovy/lang/MetaBeanProperty.java
@@ -0,0 +1,156 @@
+/*
+ * 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 groovy.lang;
+
+import org.codehaus.groovy.reflection.CachedField;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Represents a property on a bean which may have a getter and/or a setter
+ */
+public class MetaBeanProperty extends MetaProperty {
+
+ private MetaMethod getter;
+ private MetaMethod setter;
+ private CachedField field;
+
+ /**
+ * Sole constructor setting name, type (class), getter and setter.
+ */
+ public MetaBeanProperty(String name, Class type, MetaMethod getter, MetaMethod setter) {
+ super(name, type);
+ this.getter = getter;
+ this.setter = setter;
+ }
+
+ /**
+ * Get the property of the given object.
+ *
+ * @param object which to be got
+ * @return the property of the given object
+ * @throws RuntimeException if the property could not be evaluated
+ */
+ public Object getProperty(Object object) {
+ MetaMethod getter = getGetter();
+ if (getter == null) {
+ if (field != null) return field.getProperty(object);
+ //TODO: create a WriteOnlyException class?
+ throw new GroovyRuntimeException("Cannot read write-only property: " + name);
+ }
+ return getter.invoke(object, MetaClassHelper.EMPTY_ARRAY);
+ }
+
+ /**
+ * Set the property on the given object to the new value.
+ *
+ * @param object on which to set the property
+ * @param newValue the new value of the property
+ * @throws RuntimeException if the property could not be set
+ */
+ public void setProperty(Object object, Object newValue) {
+ MetaMethod setter = getSetter();
+ if (setter == null) {
+ if (field != null && !Modifier.isFinal(field.getModifiers())) {
+ field.setProperty(object, newValue);
+ return;
+ }
+ throw new GroovyRuntimeException("Cannot set read-only property: " + name);
+ }
+ newValue = DefaultTypeTransformation.castToType(newValue, getType());
+ setter.invoke(object, new Object[]{newValue});
+ }
+
+ /**
+ * Get the getter method.
+ *
+ * @return the getter method for this property.
+ */
+ public MetaMethod getGetter() {
+ return getter;
+ }
+
+ /**
+ * Get the setter method.
+ *
+ * @return the setter method for this property.
+ */
+ public MetaMethod getSetter() {
+ return setter;
+ }
+
+ /**
+ * This is for MetaClass to patch up the object later when looking for get*() methods.
+ *
+ * @param getter The getter for this property
+ */
+ void setGetter(MetaMethod getter) {
+ this.getter = getter;
+ }
+
+ /**
+ * This is for MetaClass to patch up the object later when looking for set*() methods.
+ *
+ * @param setter The setter for this property
+ */
+ void setSetter(MetaMethod setter) {
+ this.setter = setter;
+ }
+
+ /**
+ * Gets the visibility modifiers for the property as defined by the getter and setter methods.
+ *
+ * @return the visibility modifier of the getter, the setter, or both depending on which exist
+ */
+ public int getModifiers() {
+ MetaMethod getter = getGetter();
+ MetaMethod setter = getSetter();
+ if (setter != null && getter == null) return setter.getModifiers();
+ if (getter != null && setter == null) return getter.getModifiers();
+ int modifiers = getter.getModifiers() | setter.getModifiers();
+ int visibility = 0;
+ if (Modifier.isPublic(modifiers)) visibility = Modifier.PUBLIC;
+ if (Modifier.isProtected(modifiers)) visibility = Modifier.PROTECTED;
+ if (Modifier.isPrivate(modifiers)) visibility = Modifier.PRIVATE;
+ int states = getter.getModifiers() & setter.getModifiers();
+ states &= ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
+ states |= visibility;
+ return states;
+ }
+
+ /**
+ * Sets the field of this property
+ *
+ * @param field
+ */
+ public void setField(CachedField field) {
+ this.field = field;
+ }
+
+ /**
+ * Gets the field of this property
+ *
+ * @return The field of this property
+ */
+ public CachedField getField() {
+ return field;
+ }
+}