You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/14 21:56:31 UTC
[06/57] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/BytecodeInterface8.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/BytecodeInterface8.java b/src/main/java/org/codehaus/groovy/runtime/BytecodeInterface8.java
new file mode 100644
index 0000000..79fae05
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/BytecodeInterface8.java
@@ -0,0 +1,377 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import org.codehaus.groovy.runtime.metaclass.DefaultMetaClassInfo;
+
+/**
+ * This class contains methods special to optimizations used directly from bytecode in Groovy 1.8
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a>
+ */
+public class BytecodeInterface8 {
+
+ public static boolean disabledStandardMetaClass() {
+ return DefaultMetaClassInfo.disabledStandardMetaClass();
+ }
+
+ // ------------------ int ------------------
+
+ /**
+ * @return true if integer has its default MetaClass
+ */
+ public static boolean isOrigInt(){
+ return DefaultMetaClassInfo.isOrigInt();
+ }
+
+ // ------------------ int[] ------------------
+
+ /**
+ * @return true if integer array has its default MetaClass
+ */
+ public static boolean isOrigIntArray(){
+ return DefaultMetaClassInfo.isOrigIntArray();
+ }
+
+
+ /**
+ * get value from int[] using normalized index
+ */
+ public static int intArrayGet(int[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from int[] using normalized index
+ */
+ public static void intArraySet(int[] a, int i, int v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ byte ------------------
+
+ /**
+ * @return true if byte has its default MetaClass
+ */
+ public static boolean isOrigB(){
+ return DefaultMetaClassInfo.isOrigByte();
+ }
+
+ // ------------------ byte[] ------------------
+
+ /**
+ * @return true if byte array has its default MetaClass
+ */
+ public static boolean isOrigBArray(){
+ return false;
+ }
+
+
+ /**
+ * get value from byte[] using normalized index
+ */
+ public static byte bArrayGet(byte[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from byte[] using normalized index
+ */
+ public static void bArraySet(byte[] a, int i, byte v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ short ------------------
+
+ /**
+ * @return true if short has its default MetaClass
+ */
+ public static boolean isOrigS(){
+ return DefaultMetaClassInfo.isOrigShort();
+ }
+
+ // ------------------ short[] ------------------
+
+ /**
+ * @return true if short array has its default MetaClass
+ */
+ public static boolean isOrigSArray(){
+ return false;
+ }
+
+
+ /**
+ * get value from short[] using normalized index
+ */
+ public static short sArrayGet(short[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from short[] using normalized index
+ */
+ public static void sArraySet(short[] a, int i, short v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ char ------------------
+
+ /**
+ * @return true if char has its default MetaClass
+ */
+ public static boolean isOrigC(){
+ return DefaultMetaClassInfo.isOrigChar();
+ }
+
+ // ------------------ char[] ------------------
+
+ /**
+ * @return true if char array has its default MetaClass
+ */
+ public static boolean isOrigCArray(){
+ return false;
+ }
+
+
+ /**
+ * get value from char[] using normalized index
+ */
+ public static char cArrayGet(char[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from char[] using normalized index
+ */
+ public static void cArraySet(char[] a, int i, char v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ long ------------------
+
+ /**
+ * @return true if long has its default MetaClass
+ */
+ public static boolean isOrigL(){
+ return DefaultMetaClassInfo.isOrigLong();
+ }
+
+ // ------------------ long[] ------------------
+
+ /**
+ * @return true if long array has its default MetaClass
+ */
+ public static boolean isOrigLArray(){
+ return false;
+ }
+
+
+ /**
+ * get value from long[] using normalized index
+ */
+ public static long lArrayGet(long[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from long[] using normalized index
+ */
+ public static void lArraySet(long[] a, int i, long v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ boolean ------------------
+
+ /**
+ * @return true if boolean has its default MetaClass
+ */
+ public static boolean isOrigZ(){
+ return DefaultMetaClassInfo.isOrigBool();
+ }
+
+ // ------------------ boolean[] ------------------
+
+ /**
+ * @return true if boolean array has its default MetaClass
+ */
+ public static boolean isOrigZArray(){
+ return false;
+ }
+
+ /**
+ * get value from boolean[] using normalized index
+ */
+ public static boolean zArrayGet(boolean[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from boolean[] using normalized index
+ */
+ public static void zArraySet(boolean[] a, int i, boolean v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ float ------------------
+
+ /**
+ * @return true if float has its default MetaClass
+ */
+ public static boolean isOrigF(){
+ return DefaultMetaClassInfo.isOrigFloat();
+ }
+
+ // ------------------ float[] ------------------
+
+ /**
+ * @return true if float array has its default MetaClass
+ */
+ public static boolean isOrigFArray(){
+ return false;
+ }
+
+ /**
+ * get value from float[] using normalized index
+ */
+ public static float fArrayGet(float[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from float[] using normalized index
+ */
+ public static void fArraySet(float[] a, int i, float v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ double ------------------
+
+ /**
+ * @return true if double has its default MetaClass
+ */
+ public static boolean isOrigD(){
+ return DefaultMetaClassInfo.isOrigDouble();
+ }
+
+ // ------------------ double[] ------------------
+
+ /**
+ * @return true if double array has its default MetaClass
+ */
+ public static boolean isOrigDArray(){
+ return false;
+ }
+
+ /**
+ * get value from double[] using normalized index
+ */
+ public static double dArrayGet(double[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from double[] using normalized index
+ */
+ public static void dArraySet(double[] a, int i, double v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+
+ // ------------------ Object[] ------------------
+ public static Object objectArrayGet(Object[] a, int i) {
+ try {
+ return a[i];
+ } catch (Throwable t) {
+ return a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)];
+ }
+ }
+
+ /**
+ * set value from double[] using normalized index
+ */
+ public static void objectArraySet(Object[] a, int i, Object v) {
+ try {
+ a[i]=v;
+ } catch (Throwable t) {
+ a[DefaultGroovyMethodsSupport.normaliseIndex(i,a.length)]=v;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/ClassExtender.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/ClassExtender.java b/src/main/java/org/codehaus/groovy/runtime/ClassExtender.java
new file mode 100644
index 0000000..6e5d371
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/ClassExtender.java
@@ -0,0 +1,89 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A helper class used by the runtime to allow Groovy classes to be extended at runtime
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class ClassExtender {
+ private Map variables;
+ private Map methods;
+
+ public synchronized Object get(String name) {
+ if (variables != null) {
+ return variables.get(name);
+ }
+ return null;
+ }
+
+ public synchronized void set(String name, Object value) {
+ if (variables == null) {
+ variables = createMap();
+ }
+ variables.put(name, value);
+ }
+
+ public synchronized void remove(String name) {
+ if (variables != null) {
+ variables.remove(name);
+ }
+ }
+
+ public void call(String name, Object params) {
+ Closure closure = null;
+ synchronized (this) {
+ if (methods != null) {
+ closure = (Closure) methods.get(name);
+ }
+ }
+ if (closure != null) {
+ closure.call(params);
+ }
+ /*
+ else {
+ throw DoesNotUnderstandException();
+ }
+ */
+ }
+
+ public synchronized void addMethod(String name, Closure closure) {
+ if (methods == null) {
+ methods = createMap();
+ }
+ methods.put(name, methods);
+ }
+
+ public synchronized void removeMethod(String name) {
+ if (methods != null) {
+ methods.remove(name);
+ }
+ }
+
+ protected Map createMap() {
+ return new HashMap();
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/ComposedClosure.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/ComposedClosure.java b/src/main/java/org/codehaus/groovy/runtime/ComposedClosure.java
new file mode 100644
index 0000000..63e0406
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/ComposedClosure.java
@@ -0,0 +1,108 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+import java.util.List;
+
+/**
+ * A wrapper for Closure to support composition.
+ * Normally used only internally through the <code>rightShift()</code> and
+ * <code>leftShift()</code> methods on <code>Closure</code>.
+ * <p>
+ * Typical usages:
+ * <pre class="groovyTestCase">
+ * def twice = { a -> a * 2 }
+ * def inc = { b -> b + 1 }
+ * def f = { x -> twice(inc(x)) } // longhand
+ * def g = inc >> twice
+ * def h = twice << inc
+ * assert f(10) == 22
+ * assert g(10) == 22
+ * assert h(10) == 22
+ *
+ * def s2c = { it.chars[0] }
+ * def p = Integer.&toHexString >> s2c >> Character.&toUpperCase
+ * assert p(15) == 'F'
+ *
+ * def multiply = { a, b -> a * b }
+ * def identity = { a -> [a, a] }
+ * def sq = identity >> multiply
+ * assert (1..5).collect{ sq(it) } == [1, 4, 9, 16, 25]
+ *
+ * def add3 = { a, b, c -> a + b + c }
+ * def add2plus10 = add3.curry(10)
+ * def multBoth = { a, b, c -> [a*c, b*c] }
+ * def twiceBoth = multBoth.rcurry(2)
+ * def twiceBothPlus10 = twiceBoth >> add2plus10
+ * assert twiceBothPlus10(5, 10) == 40
+ * </pre>
+ *
+ * @author Paul King
+ */
+public final class ComposedClosure<V> extends Closure<V> {
+
+ private final Closure first;
+ private final Closure<V> second;
+
+ public ComposedClosure(Closure first, Closure<V> second) {
+ super(first.clone());
+ this.first = (Closure) getOwner();
+ this.second = (Closure<V>) second.clone();
+ maximumNumberOfParameters = first.getMaximumNumberOfParameters();
+ }
+
+ public void setDelegate(Object delegate) {
+ ((Closure) getOwner()).setDelegate(delegate);
+ second.setDelegate(delegate);
+ }
+
+ public Object getDelegate() {
+ return ((Closure) getOwner()).getDelegate();
+ }
+
+ public void setResolveStrategy(int resolveStrategy) {
+ ((Closure) getOwner()).setResolveStrategy(resolveStrategy);
+ second.setResolveStrategy(resolveStrategy);
+ }
+
+ public int getResolveStrategy() {
+ return ((Closure) getOwner()).getResolveStrategy();
+ }
+
+ public Object clone() {
+ return new ComposedClosure<V>(first, second);
+ }
+
+ public Class[] getParameterTypes() {
+ return first.getParameterTypes();
+ }
+
+ public Object doCall(Object... args) {
+ return call(args);
+ }
+
+ @Override
+ public V call(Object... args) {
+ Object temp = first.call(args);
+ if (temp instanceof List && second.getParameterTypes().length > 1) temp = ((List) temp).toArray();
+ return temp instanceof Object[] ? second.call((Object[]) temp) : second.call(temp);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/ConversionHandler.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/ConversionHandler.java b/src/main/java/org/codehaus/groovy/runtime/ConversionHandler.java
new file mode 100644
index 0000000..8bf7c69
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/ConversionHandler.java
@@ -0,0 +1,224 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClass;
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
+import org.codehaus.groovy.vmplugin.VMPlugin;
+import org.codehaus.groovy.vmplugin.VMPluginFactory;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class is a general adapter to map a call to a Java interface
+ * to a given delegate.
+ *
+ * @author Ben Yu
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ */
+public abstract class ConversionHandler implements InvocationHandler, Serializable {
+ private final Object delegate;
+ private static final long serialVersionUID = 1162833717190835227L;
+ private final ConcurrentHashMap<Method, Object> handleCache;
+ {
+ if (VMPluginFactory.getPlugin().getVersion() >= 7) {
+ handleCache = new ConcurrentHashMap<Method, Object>(16, 0.9f, 2);
+ } else {
+ handleCache = null;
+ }
+ }
+
+ private MetaClass metaClass;
+
+ /**
+ * Creates a ConversionHandler with an delegate.
+ *
+ * @param delegate the delegate
+ * @throws IllegalArgumentException if the given delegate is null
+ */
+ public ConversionHandler(Object delegate) {
+ if (delegate == null) {
+ throw new IllegalArgumentException("delegate must not be null");
+ }
+ this.delegate = delegate;
+ }
+
+ /**
+ * Returns the delegate.
+ *
+ * @return the delegate
+ */
+ public Object getDelegate() {
+ return delegate;
+ }
+
+ /**
+ * This method is a default implementation for the invoke method given in
+ * InvocationHandler. Any call to a method with a declaring class that is
+ * not Object, excluding toString() and default methods is redirected to invokeCustom.
+ * <p>
+ * Methods like equals and hashcode are called on the class itself instead
+ * of the delegate because they are considered fundamental methods that should
+ * not be overwritten. The toString() method gets special treatment as it is
+ * deemed to be a method that you might wish to override when called from Groovy.
+ * Interface default methods from Java 8 on the other hand are considered being
+ * default implementations you don't normally want to change. So they are called
+ * directly too
+ * </p><p>
+ * In many scenarios, it is better to overwrite the invokeCustom method where
+ * the core Object related methods are filtered out.
+ *</p>
+ * @param proxy the proxy
+ * @param method the method
+ * @param args the arguments
+ * @return the result of the invocation by method or delegate
+ * @throws Throwable if caused by the delegate or the method
+ * @see #invokeCustom(Object, Method, Object[])
+ * @see InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (handleCache != null && isDefaultMethod(method)) {
+ VMPlugin plugin = VMPluginFactory.getPlugin();
+ Object handle = handleCache.get(method);
+ if (handle == null) {
+ handle = plugin.getInvokeSpecialHandle(method, proxy);
+ handleCache.put(method, handle);
+ }
+ return plugin.invokeHandle(handle, args);
+ }
+
+ if (!checkMethod(method)) {
+ try {
+ if (method.getDeclaringClass() == GroovyObject.class) {
+ if ("getMetaClass".equals(method.getName())) {
+ return getMetaClass(proxy);
+ } else if ("setMetaClass".equals(method.getName())) {
+ return setMetaClass((MetaClass) args[0]);
+ }
+ }
+ return invokeCustom(proxy, method, args);
+ } catch (GroovyRuntimeException gre) {
+ throw ScriptBytecodeAdapter.unwrap(gre);
+ }
+ }
+
+ try {
+ return method.invoke(this, args);
+ } catch (InvocationTargetException ite) {
+ throw ite.getTargetException();
+ }
+ }
+
+ protected boolean isDefaultMethod(Method method) {
+ return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) ==
+ Modifier.PUBLIC) && method.getDeclaringClass().isInterface();
+ }
+
+ protected boolean checkMethod(Method method) {
+ return isCoreObjectMethod(method);
+ }
+
+ /**
+ * This method is called for all Methods not defined on Object.
+ * The delegate should be called here.
+ *
+ * @param proxy the proxy
+ * @param method the method
+ * @param args the arguments
+ * @return the result of the invocation of the delegate
+ * @throws Throwable any exception causes by the delegate
+ * @see #invoke(Object, Method, Object[])
+ * @see InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
+ */
+ public abstract Object invokeCustom(Object proxy, Method method, Object[] args) throws Throwable;
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ * The delegate is used if the class of the parameter and the
+ * current class are equal. In other cases the method will return
+ * false. The exact class is here used, if inheritance is needed,
+ * this method must be overwritten.
+ *
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof Proxy) {
+ obj = Proxy.getInvocationHandler(obj);
+ }
+
+ if (obj instanceof ConversionHandler) {
+ return (((ConversionHandler) obj).getDelegate()).equals(delegate);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns a hash code value for the delegate.
+ *
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return delegate.hashCode();
+ }
+
+ /**
+ * Returns a String version of the delegate.
+ *
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ return delegate.toString();
+ }
+
+ /**
+ * Checks whether a method is a core method from java.lang.Object.
+ * Such methods often receive special treatment because they are
+ * deemed fundamental enough to not be tampered with.
+ *
+ * @param method the method to check
+ * @return true if the method is deemed to be a core method
+ */
+ public static boolean isCoreObjectMethod(Method method) {
+ return Object.class.equals(method.getDeclaringClass());
+ }
+
+ private MetaClass setMetaClass(MetaClass mc) {
+ metaClass = mc;
+ return mc;
+ }
+
+ private MetaClass getMetaClass(Object proxy) {
+ MetaClass mc = metaClass;
+ if (mc == null) {
+ mc = ((MetaClassRegistryImpl) GroovySystem.getMetaClassRegistry()).getMetaClass(proxy);
+ metaClass = mc;
+ }
+ return mc;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java b/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java
new file mode 100644
index 0000000..dfc7ea0
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/ConvertedClosure.java
@@ -0,0 +1,58 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+/**
+ * This class is a general adapter to adapt a closure to any Java interface.
+ * <p>
+ * @author Ben Yu
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * Jul 27, 2006 3:50:51 PM
+ */
+public class ConvertedClosure extends ConversionHandler implements Serializable {
+ private final String methodName;
+ private static final long serialVersionUID = 1162833713450835227L;
+
+ /**
+ * to create a ConvertedClosure object.
+ * @param closure the closure object.
+ */
+ public ConvertedClosure(Closure closure, String method) {
+ super(closure);
+ this.methodName = method;
+ }
+
+ public ConvertedClosure(Closure closure) {
+ this(closure,null);
+ }
+
+ @Override
+ public Object invokeCustom(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ if (methodName!=null && !methodName.equals(method.getName())) return null;
+ return ((Closure) getDelegate()).call(args);
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/ConvertedMap.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/ConvertedMap.java b/src/main/java/org/codehaus/groovy/runtime/ConvertedMap.java
new file mode 100644
index 0000000..393d55e
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/ConvertedMap.java
@@ -0,0 +1,80 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * This class is a general adapter to adapt a map of closures to
+ * any Java interface.
+ *
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ */
+public class ConvertedMap extends ConversionHandler {
+
+ /**
+ * to create a ConvertedMap object.
+ *
+ * @param closures the map of closures
+ */
+ protected ConvertedMap(Map closures) {
+ super(closures);
+ }
+
+ @Override
+ public Object invokeCustom(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ Map m = (Map) getDelegate();
+ Closure cl = (Closure) m.get(method.getName());
+ if(cl == null && "toString".equals(method.getName())) {
+ return m.toString();
+ }
+ if (cl == null) {
+ throw new UnsupportedOperationException();
+ }
+ return cl.call(args);
+ }
+
+ @Override
+ public String toString() {
+ return DefaultGroovyMethods.toString(getDelegate());
+ }
+
+ @Override
+ protected boolean checkMethod(Method method) {
+ return isCoreObjectMethod(method);
+ }
+
+ /**
+ * Checks whether a method is a core method from java.lang.Object.
+ * Such methods often receive special treatment because they are
+ * deemed fundamental enough to not be tampered with.
+ * call toString() is an exception to allow overriding toString() by a closure specified in the map
+ *
+ * @param method the method to check
+ * @return true if the method is deemed to be a core method
+ */
+ public static boolean isCoreObjectMethod(Method method) {
+ return ConversionHandler.isCoreObjectMethod(method) && !"toString".equals(method.getName());
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/CurriedClosure.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/CurriedClosure.java b/src/main/java/org/codehaus/groovy/runtime/CurriedClosure.java
new file mode 100644
index 0000000..cabe230
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/CurriedClosure.java
@@ -0,0 +1,192 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+/**
+ * A wrapper for Closure to support currying.
+ * Normally used only internally through the <code>curry()</code>, <code>rcurry()</code> or
+ * <code>ncurry()</code> methods on <code>Closure</code>.
+ * Typical usages:
+ * <pre class="groovyTestCase">
+ * // normal usage
+ * def unitAdder = { first, second, unit -> "${first + second} $unit" }
+ * assert unitAdder(10, 15, "minutes") == "25 minutes"
+ * assert unitAdder.curry(60)(15, "minutes") == "75 minutes"
+ * def minuteAdder = unitAdder.rcurry("minutes")
+ * assert minuteAdder(15, 60) == "75 minutes"
+ *
+ * // explicit creation
+ * import org.codehaus.groovy.runtime.CurriedClosure
+ * assert new CurriedClosure(unitAdder, 45)(15, "minutes") == "60 minutes"
+ * assert new CurriedClosure(unitAdder, "six", "ty")("minutes") == "sixty minutes"
+ * </pre>
+ *
+ * Notes:
+ * <ul>
+ * <li>Caters for Groovy's lazy (rcurry) and eager (ncurry) calculation of argument position</li>
+ * </ul>
+ */
+public final class CurriedClosure<V> extends Closure<V> {
+
+ private final Object[] curriedParams;
+ private final int minParamsExpected;
+ private int index;
+ private Class varargType = null;
+
+ /**
+ * Creates the curried closure.
+ *
+ * @param index the position where the parameters should be injected (-ve for lazy)
+ * @param uncurriedClosure the closure to be called after the curried parameters are injected
+ * @param arguments the supplied parameters
+ */
+ public CurriedClosure(int index, Closure<V> uncurriedClosure, Object... arguments) {
+ super(uncurriedClosure.clone());
+ curriedParams = arguments;
+ this.index = index;
+ final int origMaxLen = uncurriedClosure.getMaximumNumberOfParameters();
+ maximumNumberOfParameters = origMaxLen - arguments.length;
+ Class[] classes = uncurriedClosure.getParameterTypes();
+ Class lastType = classes.length == 0 ? null : classes[classes.length-1];
+ if (lastType != null && lastType.isArray()) {
+ varargType = lastType;
+ }
+
+ if (!isVararg()) {
+ // perform some early param checking for non-vararg case
+ if (index < 0) {
+ // normalise
+ this.index += origMaxLen;
+ minParamsExpected = 0;
+ } else {
+ minParamsExpected = index + arguments.length;
+ }
+ if (maximumNumberOfParameters < 0) {
+ throw new IllegalArgumentException("Can't curry " + arguments.length + " arguments for a closure with " + origMaxLen + " parameters.");
+ }
+ if (index < 0) {
+ if (index < -origMaxLen || index > -arguments.length)
+ throw new IllegalArgumentException("To curry " + arguments.length + " argument(s) expect index range " +
+ (-origMaxLen) + ".." + (-arguments.length) + " but found " + index);
+ } else if (index > maximumNumberOfParameters) {
+ throw new IllegalArgumentException("To curry " + arguments.length + " argument(s) expect index range 0.." +
+ maximumNumberOfParameters + " but found " + index);
+ }
+ } else {
+ minParamsExpected = 0;
+ }
+ }
+
+ public CurriedClosure(Closure<V> uncurriedClosure, Object... arguments) {
+ this(0, uncurriedClosure, arguments);
+ }
+
+ public Object[] getUncurriedArguments(Object... arguments) {
+ if (isVararg()) {
+ int normalizedIndex = index < 0 ? index + arguments.length + curriedParams.length : index;
+ if (normalizedIndex < 0 || normalizedIndex > arguments.length) {
+ throw new IllegalArgumentException("When currying expected index range between " +
+ (-arguments.length - curriedParams.length) + ".." + (arguments.length + curriedParams.length) + " but found " + index);
+ }
+ return createNewCurriedParams(normalizedIndex, arguments);
+ }
+ if (curriedParams.length + arguments.length < minParamsExpected) {
+ throw new IllegalArgumentException("When currying expected at least " + index + " argument(s) to be supplied before known curried arguments but found " + arguments.length);
+ }
+ int newIndex = Math.min(index, curriedParams.length + arguments.length - 1);
+ // rcurried arguments are done lazily to allow normal method selection between overloaded alternatives
+ newIndex = Math.min(newIndex, arguments.length);
+ return createNewCurriedParams(newIndex, arguments);
+ }
+
+ private Object[] createNewCurriedParams(int normalizedIndex, Object[] arguments) {
+ Object[] newCurriedParams = new Object[curriedParams.length + arguments.length];
+ System.arraycopy(arguments, 0, newCurriedParams, 0, normalizedIndex);
+ System.arraycopy(curriedParams, 0, newCurriedParams, normalizedIndex, curriedParams.length);
+ if (arguments.length - normalizedIndex > 0)
+ System.arraycopy(arguments, normalizedIndex, newCurriedParams, curriedParams.length + normalizedIndex, arguments.length - normalizedIndex);
+ return newCurriedParams;
+ }
+
+ public void setDelegate(Object delegate) {
+ ((Closure) getOwner()).setDelegate(delegate);
+ }
+
+ public Object getDelegate() {
+ return ((Closure) getOwner()).getDelegate();
+ }
+
+ public void setResolveStrategy(int resolveStrategy) {
+ ((Closure) getOwner()).setResolveStrategy(resolveStrategy);
+ }
+
+ public int getResolveStrategy() {
+ return ((Closure) getOwner()).getResolveStrategy();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object clone() {
+ Closure<V> uncurriedClosure = (Closure<V>) ((Closure) getOwner()).clone();
+ return new CurriedClosure<V>(index, uncurriedClosure, curriedParams);
+ }
+
+ public Class[] getParameterTypes() {
+ Class[] oldParams = ((Closure) getOwner()).getParameterTypes();
+ int extraParams = 0;
+ int gobbledParams = curriedParams.length;
+ if (isVararg()) {
+ int numNonVarargs = oldParams.length - 1;
+ if (index < 0) {
+ int absIndex = -index;
+ // do -ve indexes based on actual args, so can't accurately calculate type here
+ // so work out minimal type params and vararg on end will allow for other possibilities
+ if (absIndex > numNonVarargs) gobbledParams = numNonVarargs;
+ int newNumNonVarargs = numNonVarargs - gobbledParams;
+ if (absIndex - curriedParams.length > newNumNonVarargs) extraParams = absIndex - curriedParams.length - newNumNonVarargs;
+ int keptParams = Math.max(numNonVarargs - absIndex, 0);
+ Class[] newParams = new Class[keptParams + newNumNonVarargs + extraParams + 1];
+ System.arraycopy(oldParams, 0, newParams, 0, keptParams);
+ for (int i = 0; i < newNumNonVarargs; i++) newParams[keptParams + i] = Object.class;
+ for (int i = 0; i < extraParams; i++) newParams[keptParams + newNumNonVarargs + i] = varargType.getComponentType();
+ newParams[newParams.length - 1] = varargType;
+ return newParams;
+ }
+ int leadingKept = Math.min(index, numNonVarargs);
+ int trailingKept = Math.max(numNonVarargs - leadingKept - curriedParams.length, 0);
+ if (index > leadingKept) extraParams = index - leadingKept;
+ Class[] newParams = new Class[leadingKept + trailingKept + extraParams + 1];
+ System.arraycopy(oldParams, 0, newParams, 0, leadingKept);
+ if (trailingKept > 0) System.arraycopy(oldParams, leadingKept + curriedParams.length, newParams, leadingKept, trailingKept);
+ for (int i = 0; i < extraParams; i++) newParams[leadingKept + trailingKept + i] = varargType.getComponentType();
+ newParams[newParams.length - 1] = varargType;
+ return newParams;
+ }
+ Class[] newParams = new Class[oldParams.length - gobbledParams + extraParams];
+ System.arraycopy(oldParams, 0, newParams, 0, index);
+ if (newParams.length - index > 0)
+ System.arraycopy(oldParams, curriedParams.length + index, newParams, index, newParams.length - index);
+ return newParams;
+ }
+
+ private boolean isVararg() {
+ return varargType != null;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/DateGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DateGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DateGroovyMethods.java
new file mode 100644
index 0000000..1c30802
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/DateGroovyMethods.java
@@ -0,0 +1,775 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyRuntimeException;
+
+import java.sql.Timestamp;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * This class defines new groovy methods which appear on normal JDK
+ * Date and Calendar classes inside the Groovy environment.
+ */
+public class DateGroovyMethods extends DefaultGroovyMethodsSupport {
+
+ /**
+ * Support the subscript operator for a Date.
+ *
+ * @param self a Date
+ * @param field a Calendar field, e.g. MONTH
+ * @return the value for the given field, e.g. FEBRUARY
+ * @see java.util.Calendar
+ * @since 1.5.5
+ */
+ public static int getAt(Date self, int field) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ return cal.get(field);
+ }
+
+ /**
+ * Convert a Date to a Calendar.
+ *
+ * @param self a Date
+ * @return a Calendar corresponding to the given Date
+ * @since 1.7.6
+ */
+ public static Calendar toCalendar(Date self) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ return cal;
+ }
+
+ /**
+ * Support the subscript operator for a Calendar.
+ *
+ * @param self a Calendar
+ * @param field a Calendar field, e.g. MONTH
+ * @return the value for the given field, e.g. FEBRUARY
+ * @see java.util.Calendar
+ * @since 1.7.3
+ */
+ public static int getAt(Calendar self, int field) {
+ return self.get(field);
+ }
+
+ /**
+ * Support the subscript operator for mutating a Calendar.
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.*
+ * def cal = Calendar.instance
+ * cal[DAY_OF_WEEK] = MONDAY
+ * cal[MONTH] = MARCH
+ * println cal.time // A Monday in March
+ * </pre>
+ *
+ * @param self A Calendar
+ * @param field A Calendar field, e.g. MONTH
+ * @param value The value for the given field, e.g. FEBRUARY
+ * @see java.util.Calendar#set(int, int)
+ * @since 1.7.3
+ */
+ public static void putAt(Calendar self, int field, int value) {
+ self.set(field, value);
+ }
+
+ /**
+ * Support the subscript operator for mutating a Date.
+ *
+ * @param self A Date
+ * @param field A Calendar field, e.g. MONTH
+ * @param value The value for the given field, e.g. FEBRUARY
+ * @see #putAt(java.util.Calendar, int, int)
+ * @see java.util.Calendar#set(int, int)
+ * @since 1.7.3
+ */
+ public static void putAt(Date self, int field, int value) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ putAt(cal, field, value);
+ self.setTime(cal.getTimeInMillis());
+ }
+
+ /**
+ * Support mutating a Calendar with a Map.
+ * <p>
+ * The map values are the normal values provided as the
+ * second parameter to <code>java.util.Calendar#set(int, int)</code>.
+ * The keys can either be the normal fields values provided as
+ * the first parameter to that method or one of the following Strings:
+ * <table border="1" cellpadding="4">
+ * <caption>Calendar index values</caption>
+ * <tr><td>year</td><td>Calendar.YEAR</td></tr>
+ * <tr><td>month</td><td>Calendar.MONTH</td></tr>
+ * <tr><td>date</td><td>Calendar.DATE</td></tr>
+ * <tr><td>dayOfMonth</td><td>Calendar.DATE</td></tr>
+ * <tr><td>hourOfDay</td><td>Calendar.HOUR_OF_DAY</td></tr>
+ * <tr><td>minute</td><td>Calendar.MINUTE</td></tr>
+ * <tr><td>second</td><td>Calendar.SECOND</td></tr>
+ * </table>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.*
+ * def cal = Calendar.instance
+ * def m = [:]
+ * m[YEAR] = 2010
+ * m[MONTH] = DECEMBER
+ * m[DATE] = 25
+ * cal.set(m)
+ * println cal.time // Christmas 2010
+ *
+ * cal.set(year:2011, month:DECEMBER, date:25)
+ * println cal.time // Christmas 2010
+ * </pre>
+ *
+ * @param self A Calendar
+ * @param updates A Map of Calendar keys and values
+ * @see java.util.Calendar#set(int, int)
+ * @see java.util.Calendar#set(int, int, int, int, int, int)
+ * @since 1.7.3
+ */
+ public static void set(Calendar self, Map<Object, Integer> updates) {
+ for (Map.Entry<Object, Integer> entry : updates.entrySet()) {
+ Object key = entry.getKey();
+ if (key instanceof String) key = CAL_MAP.get(key);
+ if (key instanceof Integer) self.set((Integer) key, entry.getValue());
+ }
+ }
+
+ /**
+ * Legacy alias for copyWith. Will be deprecated and removed in future versions of Groovy.
+ *
+ * @see #copyWith(java.util.Calendar, java.util.Map)
+ * @since 1.7.3
+ */
+ public static Calendar updated(Calendar self, Map<Object, Integer> updates) {
+ Calendar result = (Calendar) self.clone();
+ set(result, updates);
+ return result;
+ }
+
+ /**
+ * Support creating a new Date having similar properties to
+ * an existing Date (which remains unaltered) but with
+ * some fields updated according to a Map of changes.
+ * <p>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.YEAR
+ * def now = Calendar.instance
+ * def nextYear = now[YEAR] + 1
+ * def oneYearFromNow = now.copyWith(year: nextYear)
+ * println now.time
+ * println oneYearFromNow.time
+ * </pre>
+ *
+ * @param self A Calendar
+ * @param updates A Map of Calendar keys and values
+ * @return The newly created Calendar
+ * @see java.util.Calendar#set(int, int)
+ * @see java.util.Calendar#set(int, int, int, int, int, int)
+ * @see #set(java.util.Calendar, java.util.Map)
+ * @since 2.2.0
+ */
+ public static Calendar copyWith(Calendar self, Map<Object, Integer> updates) {
+ Calendar result = (Calendar) self.clone();
+ set(result, updates);
+ return result;
+ }
+
+ /**
+ * Support mutating a Date with a Map.
+ * <p>
+ * The map values are the normal values provided as the
+ * second parameter to <code>java.util.Calendar#set(int, int)</code>.
+ * The keys can either be the normal fields values provided as
+ * the first parameter to that method or one of the following Strings:
+ * <table border="1" cellpadding="4">
+ * <caption>Calendar index values</caption>
+ * <tr><td>year</td><td>Calendar.YEAR</td></tr>
+ * <tr><td>month</td><td>Calendar.MONTH</td></tr>
+ * <tr><td>date</td><td>Calendar.DATE</td></tr>
+ * <tr><td>dayOfMonth</td><td>Calendar.DATE</td></tr>
+ * <tr><td>hourOfDay</td><td>Calendar.HOUR_OF_DAY</td></tr>
+ * <tr><td>minute</td><td>Calendar.MINUTE</td></tr>
+ * <tr><td>second</td><td>Calendar.SECOND</td></tr>
+ * </table>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.YEAR
+ * def date = new Date()
+ * def nextYear = date[YEAR] + 1
+ * date.set(year: nextYear)
+ * println date
+ * </pre>
+ *
+ * @param self A Date
+ * @param updates A Map of Calendar keys and values
+ * @see java.util.Calendar#set(int, int)
+ * @see #set(java.util.Calendar, java.util.Map)
+ * @since 1.7.3
+ */
+ public static void set(Date self, Map<Object, Integer> updates) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ set(cal, updates);
+ self.setTime(cal.getTimeInMillis());
+ }
+
+ /**
+ * Legacy alias for copyWith. Will be deprecated and removed in future versions of Groovy.
+ *
+ * @see #copyWith(java.util.Date, java.util.Map)
+ * @since 1.7.3
+ */
+ public static Date updated(Date self, Map<Object, Integer> updates) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ set(cal, updates);
+ return cal.getTime();
+ }
+
+ /**
+ * Support creating a new Date having similar properties to
+ * an existing Date (which remains unaltered) but with
+ * some fields updated according to a Map of changes.
+ * <p>
+ * Example usage:
+ * <pre>
+ * import static java.util.Calendar.YEAR
+ * def today = new Date()
+ * def nextYear = today[YEAR] + 1
+ * def oneYearFromNow = today.copyWith(year: nextYear)
+ * println today
+ * println oneYearFromNow
+ * </pre>
+ *
+ * @param self A Date
+ * @param updates A Map of Calendar keys and values
+ * @return The newly created Date
+ * @see java.util.Calendar#set(int, int)
+ * @see #set(java.util.Date, java.util.Map)
+ * @see #copyWith(java.util.Calendar, java.util.Map)
+ * @since 2.2.0
+ */
+ public static Date copyWith(Date self, Map<Object, Integer> updates) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(self);
+ set(cal, updates);
+ return cal.getTime();
+ }
+
+ private static final Map<String, Integer> CAL_MAP = new HashMap<String, Integer>();
+
+ static {
+ CAL_MAP.put("year", Calendar.YEAR);
+ CAL_MAP.put("month", Calendar.MONTH);
+ CAL_MAP.put("date", Calendar.DATE);
+ CAL_MAP.put("dayOfMonth", Calendar.DATE);
+ CAL_MAP.put("hourOfDay", Calendar.HOUR_OF_DAY);
+ CAL_MAP.put("minute", Calendar.MINUTE);
+ CAL_MAP.put("second", Calendar.SECOND);
+ }
+
+ /**
+ * Increment a Date by one day.
+ *
+ * @param self a Date
+ * @return the next days date
+ * @since 1.0
+ */
+ public static Date next(Date self) {
+ return plus(self, 1);
+ }
+
+ /**
+ * Increment a Calendar by one day.
+ *
+ * @param self a Calendar
+ * @return a new Calendar set to the next day
+ * @since 1.8.7
+ */
+ public static Calendar next(Calendar self) {
+ Calendar result = (Calendar) self.clone();
+ result.add(Calendar.DAY_OF_YEAR, 1);
+ return result;
+ }
+
+ /**
+ * Decrement a Calendar by one day.
+ *
+ * @param self a Calendar
+ * @return a new Calendar set to the previous day
+ * @since 1.8.7
+ */
+ public static Calendar previous(Calendar self) {
+ Calendar result = (Calendar) self.clone();
+ result.add(Calendar.DAY_OF_YEAR, -1);
+ return result;
+ }
+
+ /**
+ * Increment a java.sql.Date by one day.
+ *
+ * @param self a java.sql.Date
+ * @return the next days date
+ * @since 1.0
+ */
+ public static java.sql.Date next(java.sql.Date self) {
+ return new java.sql.Date(next((Date) self).getTime());
+ }
+
+ /**
+ * Decrement a Date by one day.
+ *
+ * @param self a Date
+ * @return the previous days date
+ * @since 1.0
+ */
+ public static Date previous(Date self) {
+ return minus(self, 1);
+ }
+
+ /**
+ * Decrement a java.sql.Date by one day.
+ *
+ * @param self a java.sql.Date
+ * @return the previous days date
+ * @since 1.0
+ */
+ public static java.sql.Date previous(java.sql.Date self) {
+ return new java.sql.Date(previous((Date) self).getTime());
+ }
+
+ /**
+ * Add a number of days to this date and returns the new date.
+ *
+ * @param self a Date
+ * @param days the number of days to increase
+ * @return the new date
+ * @since 1.0
+ */
+ public static Date plus(Date self, int days) {
+ Calendar calendar = (Calendar) Calendar.getInstance().clone();
+ calendar.setTime(self);
+ calendar.add(Calendar.DAY_OF_YEAR, days);
+ return calendar.getTime();
+ }
+
+ /**
+ * Add a number of days to this date and returns the new date.
+ *
+ * @param self a java.sql.Date
+ * @param days the number of days to increase
+ * @return the new date
+ * @since 1.0
+ */
+ public static java.sql.Date plus(java.sql.Date self, int days) {
+ return new java.sql.Date(plus((Date) self, days).getTime());
+ }
+
+ /**
+ * Add number of days to this Timestamp and returns the new Timestamp object.
+ *
+ * @param self a Timestamp
+ * @param days the number of days to increase
+ * @return the new Timestamp
+ */
+ public static Timestamp plus(Timestamp self, int days) {
+ Calendar calendar = (Calendar) Calendar.getInstance().clone();
+ calendar.setTime(self);
+ calendar.add(Calendar.DAY_OF_YEAR, days);
+ Timestamp ts = new Timestamp(calendar.getTime().getTime());
+ ts.setNanos(self.getNanos());
+ return ts;
+ }
+
+ /**
+ * Subtract a number of days from this date and returns the new date.
+ *
+ * @param self a Date
+ * @param days the number of days to subtract
+ * @return the new date
+ * @since 1.0
+ */
+ public static Date minus(Date self, int days) {
+ return plus(self, -days);
+ }
+
+ /**
+ * Subtract a number of days from this date and returns the new date.
+ *
+ * @param self a java.sql.Date
+ * @param days the number of days to subtract
+ * @return the new date
+ * @since 1.0
+ */
+ public static java.sql.Date minus(java.sql.Date self, int days) {
+ return new java.sql.Date(minus((Date) self, days).getTime());
+ }
+
+ /**
+ * Subtract a number of days from this Timestamp and returns the new Timestamp object.
+ *
+ * @param self a Timestamp
+ * @param days the number of days to subtract
+ * @return the new Timestamp
+ */
+ public static Timestamp minus(Timestamp self, int days) {
+ return plus(self, -days);
+ }
+
+ /**
+ * Subtract another date from this one and return the number of days of the difference.
+ * <p>
+ * Date self = Date then + (Date self - Date then)
+ * <p>
+ * IOW, if self is before then the result is a negative value.
+ *
+ * @param self a Calendar
+ * @param then another Calendar
+ * @return number of days
+ * @since 1.6.0
+ */
+ public static int minus(Calendar self, Calendar then) {
+ Calendar a = self;
+ Calendar b = then;
+
+ boolean swap = a.before(b);
+
+ if (swap) {
+ Calendar t = a;
+ a = b;
+ b = t;
+ }
+
+ int days = 0;
+
+ b = (Calendar) b.clone();
+
+ while (a.get(Calendar.YEAR) > b.get(Calendar.YEAR)) {
+ days += 1 + (b.getActualMaximum(Calendar.DAY_OF_YEAR) - b.get(Calendar.DAY_OF_YEAR));
+ b.set(Calendar.DAY_OF_YEAR, 1);
+ b.add(Calendar.YEAR, 1);
+ }
+
+ days += a.get(Calendar.DAY_OF_YEAR) - b.get(Calendar.DAY_OF_YEAR);
+
+ if (swap) days = -days;
+
+ return days;
+ }
+
+ /**
+ * Subtract another Date from this one and return the number of days of the difference.
+ * <p>
+ * Date self = Date then + (Date self - Date then)
+ * <p>
+ * IOW, if self is before then the result is a negative value.
+ *
+ * @param self a Date
+ * @param then another Date
+ * @return number of days
+ * @since 1.6.0
+ */
+ public static int minus(Date self, Date then) {
+ Calendar a = (Calendar) Calendar.getInstance().clone();
+ a.setTime(self);
+ Calendar b = (Calendar) Calendar.getInstance().clone();
+ b.setTime(then);
+ return minus(a, b);
+ }
+
+ /**
+ * <p>Create a String representation of this date according to the given
+ * format pattern.
+ * <p>
+ * <p>For example, if the system timezone is GMT,
+ * <code>new Date(0).format('MM/dd/yy')</code> would return the string
+ * <code>"01/01/70"</code>. See documentation for {@link java.text.SimpleDateFormat}
+ * for format pattern use.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @param format the format pattern to use according to {@link java.text.SimpleDateFormat}
+ * @return a string representation of this date.
+ * @see java.text.SimpleDateFormat
+ * @since 1.5.7
+ */
+ public static String format(Date self, String format) {
+ return new SimpleDateFormat(format).format(self);
+ }
+
+ /**
+ * <p>Create a String representation of this date according to the given
+ * format pattern and timezone.
+ * <p>
+ * <p>For example:
+ * <code>
+ * def d = new Date(0)
+ * def tz = TimeZone.getTimeZone('GMT')
+ * println d.format('dd/MMM/yyyy', tz)
+ * </code> would return the string
+ * <code>"01/Jan/1970"</code>. See documentation for {@link java.text.SimpleDateFormat}
+ * for format pattern use.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @param format the format pattern to use according to {@link java.text.SimpleDateFormat}
+ * @param tz the TimeZone to use
+ * @return a string representation of this date.
+ * @see java.text.SimpleDateFormat
+ * @since 1.8.3
+ */
+ public static String format(Date self, String format, TimeZone tz) {
+ SimpleDateFormat sdf = new SimpleDateFormat(format);
+ sdf.setTimeZone(tz);
+ return sdf.format(self);
+ }
+
+ /**
+ * <p>Return a string representation of the 'day' portion of this date
+ * according to the locale-specific {@link java.text.DateFormat#SHORT} default format.
+ * For an "en_UK" system locale, this would be <code>dd/MM/yy</code>.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @return a string representation of this date
+ * @see java.text.DateFormat#getDateInstance(int)
+ * @see java.text.DateFormat#SHORT
+ * @since 1.5.7
+ */
+ public static String getDateString(Date self) {
+ return DateFormat.getDateInstance(DateFormat.SHORT).format(self);
+ }
+
+ /**
+ * <p>Return a string representation of the time portion of this date
+ * according to the locale-specific {@link java.text.DateFormat#MEDIUM} default format.
+ * For an "en_UK" system locale, this would be <code>HH:MM:ss</code>.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @return a string representing the time portion of this date
+ * @see java.text.DateFormat#getTimeInstance(int)
+ * @see java.text.DateFormat#MEDIUM
+ * @since 1.5.7
+ */
+ public static String getTimeString(Date self) {
+ return DateFormat.getTimeInstance(DateFormat.MEDIUM).format(self);
+ }
+
+ /**
+ * <p>Return a string representation of the date and time time portion of
+ * this Date instance, according to the locale-specific format used by
+ * {@link java.text.DateFormat}. This method uses the {@link java.text.DateFormat#SHORT}
+ * preset for the day portion and {@link java.text.DateFormat#MEDIUM} for the time
+ * portion of the output string.
+ * <p>
+ * <p>Note that a new DateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self a Date
+ * @return a string representation of this date and time
+ * @see java.text.DateFormat#getDateTimeInstance(int, int)
+ * @since 1.5.7
+ */
+ public static String getDateTimeString(Date self) {
+ return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(self);
+ }
+
+ /**
+ * Common code for {@link #clearTime(java.util.Calendar)} and {@link #clearTime(java.util.Date)}
+ * and {@link #clearTime(java.sql.Date)}
+ *
+ * @param self a Calendar to adjust
+ */
+ private static void clearTimeCommon(final Calendar self) {
+ self.set(Calendar.HOUR_OF_DAY, 0);
+ self.clear(Calendar.MINUTE);
+ self.clear(Calendar.SECOND);
+ self.clear(Calendar.MILLISECOND);
+ }
+
+ /**
+ * Clears the time portion of this Date instance; useful utility where
+ * it makes sense to compare month/day/year only portions of a Date.
+ *
+ * @param self a Date
+ * @return the Date but with the time portion cleared
+ * @since 1.6.7
+ */
+ public static Date clearTime(final Date self) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(self);
+ clearTimeCommon(calendar);
+ self.setTime(calendar.getTime().getTime());
+ return self;
+ }
+
+ /**
+ * Clears the time portion of this java.sql.Date instance; useful utility
+ * where it makes sense to compare month/day/year only portions of a Date.
+ *
+ * @param self a java.sql.Date
+ * @return the java.sql.Date but with the time portion cleared
+ * @since 1.6.7
+ */
+ public static java.sql.Date clearTime(final java.sql.Date self) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(self);
+ clearTimeCommon(calendar);
+ self.setTime(calendar.getTime().getTime());
+ return self;
+ }
+
+ /**
+ * Clears the time portion of this Calendar instance; useful utility
+ * where it makes sense to compare month/day/year only portions of a Calendar.
+ *
+ * @param self a Calendar
+ * @return the Calendar but with the time portion cleared
+ * @since 1.6.7
+ */
+ public static Calendar clearTime(final Calendar self) {
+ clearTimeCommon(self);
+ return self;
+ }
+
+ /**
+ * <p>Shortcut for {@link java.text.SimpleDateFormat} to output a String representation
+ * of this calendar instance. This method respects the Calendar's assigned
+ * {@link java.util.TimeZone}, whereas calling <code>cal.time.format('HH:mm:ss')</code>
+ * would use the system timezone.
+ * <p>Note that Calendar equivalents of <code>date.getDateString()</code>
+ * and variants do not exist because those methods are Locale-dependent.
+ * Although a Calendar may be assigned a {@link java.util.Locale}, that information is
+ * lost and therefore cannot be used to control the default date/time formats
+ * provided by these methods. Instead, the system Locale would always be
+ * used. The alternative is to simply call
+ * {@link java.text.DateFormat#getDateInstance(int, java.util.Locale)} and pass the same Locale
+ * that was used for the Calendar.
+ *
+ * @param self this calendar
+ * @param pattern format pattern
+ * @return String representation of this calendar with the given format.
+ * @see java.text.DateFormat#setTimeZone(java.util.TimeZone)
+ * @see java.text.SimpleDateFormat#format(java.util.Date)
+ * @see #format(java.util.Date, String)
+ * @since 1.6.0
+ */
+ public static String format(Calendar self, String pattern) {
+ SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+ sdf.setTimeZone(self.getTimeZone());
+ return sdf.format(self.getTime());
+ }
+
+ /**
+ * Iterates from this date up to the given date, inclusive,
+ * incrementing by one day each time.
+ *
+ * @param self a Date
+ * @param to another Date to go up to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void upto(Date self, Date to, Closure closure) {
+ if (self.compareTo(to) <= 0) {
+ for (Date i = (Date) self.clone(); i.compareTo(to) <= 0; i = next(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to upto() cannot be earlier than the value (" + self + ") it's called on.");
+ }
+
+ /**
+ * Iterates from the date represented by this calendar up to the date represented
+ * by the given calendar, inclusive, incrementing by one day each time.
+ *
+ * @param self a Calendar
+ * @param to another Calendar to go up to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void upto(Calendar self, Calendar to, Closure closure) {
+ if (self.compareTo(to) <= 0) {
+ for (Calendar i = (Calendar) self.clone(); i.compareTo(to) <= 0; i = next(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to upto() cannot be earlier than the value (" + self + ") it's called on.");
+ }
+
+ /**
+ * Iterates from this date down to the given date, inclusive,
+ * decrementing by one day each time.
+ *
+ * @param self a Date
+ * @param to another Date to go down to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void downto(Date self, Date to, Closure closure) {
+ if (self.compareTo(to) >= 0) {
+ for (Date i = (Date) self.clone(); i.compareTo(to) >= 0; i = previous(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to downto() cannot be later than the value (" + self + ") it's called on.");
+ }
+
+ /**
+ * Iterates from the date represented by this calendar up to the date represented
+ * by the given calendar, inclusive, incrementing by one day each time.
+ *
+ * @param self a Calendar
+ * @param to another Calendar to go down to
+ * @param closure the closure to call
+ * @since 2.2
+ */
+ public static void downto(Calendar self, Calendar to, Closure closure) {
+ if (self.compareTo(to) >= 0) {
+ for (Calendar i = (Calendar) self.clone(); i.compareTo(to) >= 0; i = previous(i)) {
+ closure.call(i);
+ }
+ } else
+ throw new GroovyRuntimeException("The argument (" + to +
+ ") to downto() cannot be later than the value (" + self + ") it's called on.");
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/runtime/DefaultCachedMethodKey.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultCachedMethodKey.java b/src/main/java/org/codehaus/groovy/runtime/DefaultCachedMethodKey.java
new file mode 100644
index 0000000..9dd8cd0
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultCachedMethodKey.java
@@ -0,0 +1,47 @@
+/*
+ * 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.codehaus.groovy.runtime;
+
+import org.codehaus.groovy.reflection.CachedClass;
+
+
+/**
+ * A default implementation of MethodKey
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class DefaultCachedMethodKey extends MethodKey{
+
+ private final CachedClass[] parameterTypes;
+
+ public DefaultCachedMethodKey(Class sender, String name, CachedClass[] parameterTypes, boolean isCallToSuper) {
+ super(sender, name,isCallToSuper);
+ this.parameterTypes = parameterTypes;
+ }
+
+ public int getParameterCount() {
+ return parameterTypes.length;
+ }
+
+ public Class getParameterType(int index) {
+ CachedClass c = parameterTypes[index];
+ if (c==null) return Object.class;
+ return c.getTheClass();
+ }
+}