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/17 14:01:15 UTC
[04/62] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
new file mode 100644
index 0000000..3838fdc
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java
@@ -0,0 +1,383 @@
+/*
+ * 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.EmptyRange;
+import groovy.lang.IntRange;
+import groovy.lang.Range;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Hashtable;
+import java.util.IdentityHashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Properties;
+import java.util.Queue;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.Stack;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Vector;
+import java.util.WeakHashMap;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.DelayQueue;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.PriorityBlockingQueue;
+import java.util.concurrent.SynchronousQueue;
+import java.util.logging.Logger;
+
+/**
+ * Support methods for DefaultGroovyMethods and PluginDefaultMethods.
+ */
+public class DefaultGroovyMethodsSupport {
+
+ private static final Logger LOG = Logger.getLogger(DefaultGroovyMethodsSupport.class.getName());
+ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+ // helper method for getAt and putAt
+ protected static RangeInfo subListBorders(int size, Range range) {
+ if (range instanceof IntRange) {
+ return ((IntRange)range).subListBorders(size);
+ }
+ int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), size);
+ int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), size);
+ boolean reverse = range.isReverse();
+ if (from > to) {
+ // support list[1..-1]
+ int tmp = to;
+ to = from;
+ from = tmp;
+ reverse = !reverse;
+ }
+ return new RangeInfo(from, to + 1, reverse);
+ }
+
+ // helper method for getAt and putAt
+ protected static RangeInfo subListBorders(int size, EmptyRange range) {
+ int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), size);
+ return new RangeInfo(from, from, false);
+ }
+
+ /**
+ * This converts a possibly negative index to a real index into the array.
+ *
+ * @param i the unnormalized index
+ * @param size the array size
+ * @return the normalised index
+ */
+ protected static int normaliseIndex(int i, int size) {
+ int temp = i;
+ if (i < 0) {
+ i += size;
+ }
+ if (i < 0) {
+ throw new ArrayIndexOutOfBoundsException("Negative array index [" + temp + "] too large for array size " + size);
+ }
+ return i;
+ }
+
+ /**
+ * Close the Closeable. Logging a warning if any problems occur.
+ *
+ * @param closeable the thing to close
+ */
+ public static void closeWithWarning(Closeable closeable) {
+ tryClose(closeable, true); // ignore result
+ }
+
+ /**
+ * Attempts to close the closeable returning rather than throwing
+ * any Exception that may occur.
+ *
+ * @param closeable the thing to close
+ * @param logWarning if true will log a warning if an exception occurs
+ * @return throwable Exception from the close method, else null
+ */
+ static Throwable tryClose(AutoCloseable closeable, boolean logWarning) {
+ Throwable thrown = null;
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (Exception e) {
+ thrown = e;
+ if (logWarning) {
+ LOG.warning("Caught exception during close(): " + e);
+ }
+ }
+ }
+ return thrown;
+ }
+
+ /**
+ * Close the Closeable. Ignore any problems that might occur.
+ *
+ * @param c the thing to close
+ */
+ public static void closeQuietly(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ /* ignore */
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <T> Collection<T> cloneSimilarCollection(Collection<T> orig, int newCapacity) {
+ Collection<T> answer = (Collection<T>) cloneObject(orig);
+ if (answer != null) return answer;
+
+ // fall back to creation
+ answer = createSimilarCollection(orig, newCapacity);
+ answer.addAll(orig);
+ return answer;
+ }
+
+ private static Object cloneObject(Object orig) {
+ if (orig instanceof Cloneable) {
+ try {
+ return InvokerHelper.invokeMethod(orig, "clone", EMPTY_OBJECT_ARRAY);
+ } catch (Exception ex) {
+ // ignore
+ }
+ }
+ return null;
+ }
+
+ protected static Collection createSimilarOrDefaultCollection(Object object) {
+ if (object instanceof Collection) {
+ return createSimilarCollection((Collection<?>) object);
+ }
+ return new ArrayList();
+ }
+
+ protected static <T> Collection<T> createSimilarCollection(Iterable<T> iterable) {
+ if (iterable instanceof Collection) {
+ return createSimilarCollection((Collection<T>) iterable);
+ } else {
+ return new ArrayList<T>();
+ }
+ }
+
+ protected static <T> Collection<T> createSimilarCollection(Collection<T> collection) {
+ return createSimilarCollection(collection, collection.size());
+ }
+
+ protected static <T> Collection<T> createSimilarCollection(Collection<T> orig, int newCapacity) {
+ if (orig instanceof Set) {
+ return createSimilarSet((Set<T>) orig);
+ }
+ if (orig instanceof List) {
+ return createSimilarList((List<T>) orig, newCapacity);
+ }
+ if (orig instanceof Queue) {
+ return createSimilarQueue((Queue<T>) orig);
+ }
+ return new ArrayList<T>(newCapacity);
+ }
+
+ protected static <T> List<T> createSimilarList(List<T> orig, int newCapacity) {
+ if (orig instanceof LinkedList)
+ return new LinkedList<T>();
+
+ if (orig instanceof Stack)
+ return new Stack<T>();
+
+ if (orig instanceof Vector)
+ return new Vector<T>();
+
+ if (orig instanceof CopyOnWriteArrayList)
+ return new CopyOnWriteArrayList<T>();
+
+ return new ArrayList<T>(newCapacity);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <T> T[] createSimilarArray(T[] orig, int newCapacity) {
+ Class<T> componentType = (Class<T>) orig.getClass().getComponentType();
+ return (T[]) Array.newInstance(componentType, newCapacity);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <T> Set<T> createSimilarSet(Set<T> orig) {
+ if (orig instanceof SortedSet) {
+ Comparator comparator = ((SortedSet) orig).comparator();
+ if (orig instanceof ConcurrentSkipListSet) {
+ return new ConcurrentSkipListSet<T>(comparator);
+ } else {
+ return new TreeSet<T>(comparator);
+ }
+ } else {
+ if (orig instanceof CopyOnWriteArraySet) {
+ return new CopyOnWriteArraySet<T>();
+ } else {
+ // Do not use HashSet
+ return new LinkedHashSet<T>();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <T> Queue<T> createSimilarQueue(Queue<T> orig) {
+ if (orig instanceof ArrayBlockingQueue) {
+ ArrayBlockingQueue queue = (ArrayBlockingQueue) orig;
+ return new ArrayBlockingQueue<T>(queue.size() + queue.remainingCapacity());
+ } else if (orig instanceof ArrayDeque) {
+ return new ArrayDeque<T>();
+ } else if (orig instanceof ConcurrentLinkedQueue) {
+ return new ConcurrentLinkedQueue<T>();
+ } else if (orig instanceof DelayQueue) {
+ return new DelayQueue();
+ } else if (orig instanceof LinkedBlockingDeque) {
+ return new LinkedBlockingDeque<T>();
+ } else if (orig instanceof LinkedBlockingQueue) {
+ return new LinkedBlockingQueue<T>();
+ } else if (orig instanceof PriorityBlockingQueue) {
+ return new PriorityBlockingQueue<T>();
+ } else if (orig instanceof PriorityQueue) {
+ return new PriorityQueue<T>(11, ((PriorityQueue) orig).comparator());
+ } else if (orig instanceof SynchronousQueue) {
+ return new SynchronousQueue<T>();
+ } else {
+ return new LinkedList<T>();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <K, V> Map<K, V> createSimilarMap(Map<K, V> orig) {
+ if (orig instanceof SortedMap) {
+ Comparator comparator = ((SortedMap) orig).comparator();
+ if (orig instanceof ConcurrentSkipListMap) {
+ return new ConcurrentSkipListMap<K, V>(comparator);
+ } else {
+ return new TreeMap<K, V>(comparator);
+ }
+ } else {
+ if (orig instanceof ConcurrentHashMap) {
+ return new ConcurrentHashMap<K, V>();
+ } else if (orig instanceof Hashtable) {
+ if (orig instanceof Properties) {
+ return (Map<K, V>) new Properties();
+ } else {
+ return new Hashtable<K, V>();
+ }
+ } else if (orig instanceof IdentityHashMap) {
+ return new IdentityHashMap<K, V>();
+ } else if (orig instanceof WeakHashMap) {
+ return new WeakHashMap<K, V>();
+ } else {
+ // Do not use HashMap
+ return new LinkedHashMap<K, V>();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static <K, V> Map<K ,V> cloneSimilarMap(Map<K, V> orig) {
+ Map<K, V> answer = (Map<K, V>) cloneObject(orig);
+ if (answer != null) return answer;
+
+ // fall back to some defaults
+ if (orig instanceof SortedMap) {
+ if (orig instanceof ConcurrentSkipListMap) {
+ return new ConcurrentSkipListMap<K, V>(orig);
+ } else {
+ return new TreeMap<K, V>(orig);
+ }
+ } else {
+ if (orig instanceof ConcurrentHashMap) {
+ return new ConcurrentHashMap<K, V>(orig);
+ } else if (orig instanceof Hashtable) {
+ if (orig instanceof Properties) {
+ Map<K, V> map = (Map<K, V>) new Properties();
+ map.putAll(orig);
+ return map;
+ } else {
+ return new Hashtable<K, V>(orig);
+ }
+ } else if (orig instanceof IdentityHashMap) {
+ return new IdentityHashMap<K, V>(orig);
+ } else if (orig instanceof WeakHashMap) {
+ return new WeakHashMap<K, V>(orig);
+ } else {
+ // Do not use HashMap
+ return new LinkedHashMap<K, V>(orig);
+ }
+ }
+ }
+
+ /**
+ * Determines if all items of this array are of the same type.
+ *
+ * @param cols an array of collections
+ * @return true if the collections are all of the same type
+ */
+ @SuppressWarnings("unchecked")
+ protected static boolean sameType(Collection[] cols) {
+ List all = new LinkedList();
+ for (Collection col : cols) {
+ all.addAll(col);
+ }
+ if (all.isEmpty())
+ return true;
+
+ Object first = all.get(0);
+
+ //trying to determine the base class of the collections
+ //special case for Numbers
+ Class baseClass;
+ if (first instanceof Number) {
+ baseClass = Number.class;
+ } else if (first == null) {
+ baseClass = NullObject.class;
+ } else {
+ baseClass = first.getClass();
+ }
+
+ for (Collection col : cols) {
+ for (Object o : col) {
+ if (!baseClass.isInstance(o)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
new file mode 100644
index 0000000..1c85e91
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
@@ -0,0 +1,314 @@
+/*
+ * 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 org.codehaus.groovy.reflection.ReflectionUtils;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.TimeZone;
+import java.util.regex.Matcher;
+
+/**
+ * This class defines all the new static groovy methods which appear on normal
+ * JDK classes inside the Groovy environment. Static methods are used with the
+ * first parameter as the destination class.
+ *
+ * @author Guillaume Laforge
+ * @author Dierk Koenig
+ * @author Joachim Baumann
+ * @author Paul King
+ * @author Kent Inge Fagerland Simonsen
+ */
+public class DefaultGroovyStaticMethods {
+
+ /**
+ * Start a Thread with the given closure as a Runnable instance.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param closure the Runnable closure
+ * @return the started thread
+ * @since 1.0
+ */
+ public static Thread start(Thread self, Closure closure) {
+ return createThread(null, false, closure);
+ }
+
+ /**
+ * Start a Thread with a given name and the given closure
+ * as a Runnable instance.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param name the name to give the thread
+ * @param closure the Runnable closure
+ * @return the started thread
+ * @since 1.6
+ */
+ public static Thread start(Thread self, String name, Closure closure) {
+ return createThread(name, false, closure);
+ }
+
+ /**
+ * Start a daemon Thread with the given closure as a Runnable instance.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param closure the Runnable closure
+ * @return the started thread
+ * @since 1.0
+ */
+ public static Thread startDaemon(Thread self, Closure closure) {
+ return createThread(null, true, closure);
+ }
+
+ /**
+ * Start a daemon Thread with a given name and the given closure as
+ * a Runnable instance.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param name the name to give the thread
+ * @param closure the Runnable closure
+ * @return the started thread
+ * @since 1.6
+ */
+ public static Thread startDaemon(Thread self, String name, Closure closure) {
+ return createThread(name, true, closure);
+ }
+
+ private static Thread createThread(String name, boolean daemon, Closure closure) {
+ Thread thread = name != null ? new Thread(closure, name) : new Thread(closure);
+ if (daemon) thread.setDaemon(true);
+ thread.start();
+ return thread;
+ }
+
+ /**
+ * Get the last hidden matcher that the system used to do a match.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @return the last regex matcher
+ * @since 1.0
+ */
+ public static Matcher getLastMatcher(Matcher self) {
+ return RegexSupport.getLastMatcher();
+ }
+
+ /**
+ * This method is used by both sleep() methods to implement sleeping
+ * for the given time even if interrupted
+ *
+ * @param millis the number of milliseconds to sleep
+ * @param closure optional closure called when interrupted
+ * as long as the closure returns false the sleep continues
+ */
+ private static void sleepImpl(long millis, Closure closure) {
+ long start = System.currentTimeMillis();
+ long rest = millis;
+ long current;
+ while (rest > 0) {
+ try {
+ Thread.sleep(rest);
+ rest = 0;
+ } catch (InterruptedException e) {
+ if (closure != null) {
+ if (DefaultTypeTransformation.castToBoolean(closure.call(e))) {
+ return;
+ }
+ }
+ current = System.currentTimeMillis(); // compensate for closure's time
+ rest = millis + start - current;
+ }
+ }
+ }
+
+ /**
+ * Sleep for so many milliseconds, even if interrupted.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param milliseconds the number of milliseconds to sleep
+ * @since 1.0
+ */
+ public static void sleep(Object self, long milliseconds) {
+ sleepImpl(milliseconds, null);
+ }
+
+ /**
+ * Sleep for so many milliseconds, using a given closure for interrupt processing.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param milliseconds the number of milliseconds to sleep
+ * @param onInterrupt interrupt handler, InterruptedException is passed to the Closure
+ * as long as it returns false, the sleep continues
+ * @since 1.0
+ */
+ public static void sleep(Object self, long milliseconds, Closure onInterrupt) {
+ sleepImpl(milliseconds, onInterrupt);
+ }
+
+ /**
+ * Parse a String into a Date instance using the given pattern.
+ * This convenience method acts as a wrapper for {@link java.text.SimpleDateFormat}.
+ * <p>
+ * Note that a new SimpleDateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param format pattern used to parse the input string.
+ * @param input String to be parsed to create the date instance
+ * @return a new Date instance representing the parsed input string
+ * @throws ParseException if there is a parse error
+ * @see java.text.SimpleDateFormat#parse(java.lang.String)
+ * @since 1.5.7
+ */
+ public static Date parse(Date self, String format, String input) throws ParseException {
+ return new SimpleDateFormat(format).parse(input);
+ }
+
+ /**
+ * Parse a String into a Date instance using the given pattern and TimeZone.
+ * This convenience method acts as a wrapper for {@link java.text.SimpleDateFormat}.
+ * <p>
+ * Note that a new SimpleDateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param format pattern used to parse the input string.
+ * @param input String to be parsed to create the date instance
+ * @param zone TimeZone to use when parsing
+ * @return a new Date instance representing the parsed input string
+ * @throws ParseException if there is a parse error
+ * @see java.text.SimpleDateFormat#parse(java.lang.String)
+ * @since 2.4.1
+ */
+ public static Date parse(Date self, String format, String input, TimeZone zone) throws ParseException {
+ SimpleDateFormat sdf = new SimpleDateFormat(format);
+ sdf.setTimeZone(zone);
+ return sdf.parse(input);
+ }
+
+ /**
+ * Parse a String matching the pattern EEE MMM dd HH:mm:ss zzz yyyy
+ * containing US-locale-constants only (e.g. Sat for Saturdays).
+ * Such a string is generated by the toString method of {@link java.util.Date}
+ * <p>
+ * Note that a new SimpleDateFormat instance is created for every
+ * invocation of this method (for thread safety).
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param dateToString String to be parsed to create the date instance. Must match the pattern EEE MMM dd HH:mm:ss zzz yyyy with US-locale symbols
+ * @return a new Date instance representing the parsed input string
+ * @throws ParseException if there is a parse error
+ */
+ public static Date parseToStringDate(Date self, String dateToString) throws ParseException {
+ return new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US).parse(dateToString);
+ }
+
+ /**
+ * Works exactly like ResourceBundle.getBundle(String). This is needed
+ * because the java method depends on a particular stack configuration that
+ * is not guaranteed in Groovy when calling the Java method.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param bundleName the name of the bundle.
+ * @return the resource bundle
+ * @see java.util.ResourceBundle#getBundle(java.lang.String)
+ * @since 1.6.0
+ */
+ public static ResourceBundle getBundle(ResourceBundle self, String bundleName) {
+ return getBundle(self, bundleName, Locale.getDefault());
+ }
+
+ /**
+ * Works exactly like ResourceBundle.getBundle(String, Locale). This is needed
+ * because the java method depends on a particular stack configuration that
+ * is not guaranteed in Groovy when calling the Java method.
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @param bundleName the name of the bundle.
+ * @param locale the specific locale
+ * @return the resource bundle
+ * @see java.util.ResourceBundle#getBundle(java.lang.String, java.util.Locale)
+ * @since 1.6.0
+ */
+ public static ResourceBundle getBundle(ResourceBundle self, String bundleName, Locale locale) {
+ Class c = ReflectionUtils.getCallingClass();
+ ClassLoader targetCL = c != null ? c.getClassLoader() : null;
+ if (targetCL == null) targetCL = ClassLoader.getSystemClassLoader();
+ return ResourceBundle.getBundle(bundleName, locale, targetCL);
+ }
+
+ public static File createTempDir(File self) throws IOException {
+ return createTempDir(self, "groovy-generated-", "-tmpdir");
+ }
+
+ public static File createTempDir(File self, final String prefix, final String suffix) throws IOException {
+ final int MAXTRIES = 3;
+ int accessDeniedCounter = 0;
+ File tempFile=null;
+ for (int i=0; i<MAXTRIES; i++) {
+ try {
+ tempFile = File.createTempFile(prefix, suffix);
+ tempFile.delete();
+ tempFile.mkdirs();
+ break;
+ } catch (IOException ioe) {
+ if (ioe.getMessage().startsWith("Access is denied")) {
+ accessDeniedCounter++;
+ try { Thread.sleep(100); } catch (InterruptedException e) {}
+ }
+ if (i==MAXTRIES-1) {
+ if (accessDeniedCounter==MAXTRIES) {
+ String msg =
+ "Access is denied.\nWe tried " +
+ + accessDeniedCounter+
+ " times to create a temporary directory"+
+ " and failed each time. If you are on Windows"+
+ " you are possibly victim to"+
+ " http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6325169. "+
+ " this is no bug in Groovy.";
+ throw new IOException(msg);
+ } else {
+ throw ioe;
+ }
+ }
+ continue;
+ }
+ }
+ return tempFile;
+ }
+
+ /**
+ * Get the current time in seconds
+ *
+ * @param self placeholder variable used by Groovy categories; ignored for default static methods
+ * @return the difference, measured in seconds, between
+ * the current time and midnight, January 1, 1970 UTC.
+ * @see System#currentTimeMillis()
+ */
+ public static long currentTimeSeconds(System self){
+ return System.currentTimeMillis() / 1000;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/DefaultMethodKey.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultMethodKey.java b/src/main/java/org/codehaus/groovy/runtime/DefaultMethodKey.java
new file mode 100644
index 0000000..96612ff
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultMethodKey.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * A default implementation of MethodKey
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ */
+public class DefaultMethodKey extends MethodKey{
+
+ private final Class[] parameterTypes;
+
+ public DefaultMethodKey(Class sender, String name, Class[] parameterTypes, boolean isCallToSuper) {
+ super(sender, name,isCallToSuper);
+ this.parameterTypes = parameterTypes;
+ }
+
+ public int getParameterCount() {
+ return parameterTypes.length;
+ }
+
+ public Class getParameterType(int index) {
+ Class c = parameterTypes[index];
+ if (c==null) return Object.class;
+ return c;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java
new file mode 100644
index 0000000..62b6eeb
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethods.java
@@ -0,0 +1,367 @@
+/*
+ * 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.StringWriterIOException;
+import groovy.lang.Writable;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+
+import static org.codehaus.groovy.runtime.EncodingGroovyMethodsSupport.TRANSLATE_TABLE;
+import static org.codehaus.groovy.runtime.EncodingGroovyMethodsSupport.TRANSLATE_TABLE_URLSAFE;
+
+/**
+ * This class defines all the encoding/decoding groovy methods which enhance
+ * the normal JDK classes when inside the Groovy environment.
+ * Static methods are used with the first parameter the destination class.
+ */
+public class EncodingGroovyMethods {
+
+ private static final char[] T_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
+
+ private static final char[] T_TABLE_URLSAFE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=".toCharArray();
+
+ private static final String CHUNK_SEPARATOR = "\r\n";
+
+ /**
+ * Produce a Writable object which writes the Base64 encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 encoding and chunking see <code>RFC 4648</code>.
+ *
+ * @param data Byte array to be encoded
+ * @param chunked whether or not the Base64 encoded data should be MIME chunked
+ * @return object which will write the Base64 encoding of the byte array
+ * @since 1.5.1
+ */
+ public static Writable encodeBase64(Byte[] data, final boolean chunked) {
+ return encodeBase64(DefaultTypeTransformation.convertToByteArray(data), chunked);
+ }
+
+ /**
+ * Produce a Writable object which writes the Base64 encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 encoding and chunking see <code>RFC 4648</code>.
+ *
+ * @param data Byte array to be encoded
+ * @return object which will write the Base64 encoding of the byte array
+ * @since 1.0
+ */
+ public static Writable encodeBase64(Byte[] data) {
+ return encodeBase64(DefaultTypeTransformation.convertToByteArray(data), false);
+ }
+
+ /**
+ * Produce a Writable object which writes the Base64 encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 encoding and chunking see <code>RFC 4648</code>.
+ *
+ * @param data byte array to be encoded
+ * @param chunked whether or not the Base64 encoded data should be MIME chunked
+ * @return object which will write the Base64 encoding of the byte array
+ * @since 1.5.7
+ */
+ public static Writable encodeBase64(final byte[] data, final boolean chunked) {
+ return encodeBase64(data, chunked, false, true);
+ }
+
+ private static Writable encodeBase64(final byte[] data, final boolean chunked, final boolean urlSafe, final boolean pad) {
+ return new Writable() {
+ public Writer writeTo(final Writer writer) throws IOException {
+ int charCount = 0;
+ final int dLimit = (data.length / 3) * 3;
+ final char[] table = urlSafe ? T_TABLE_URLSAFE : T_TABLE;
+ for (int dIndex = 0; dIndex != dLimit; dIndex += 3) {
+ int d = ((data[dIndex] & 0XFF) << 16) | ((data[dIndex + 1] & 0XFF) << 8) | (data[dIndex + 2] & 0XFF);
+
+ writer.write(table[d >> 18]);
+ writer.write(table[(d >> 12) & 0X3F]);
+ writer.write(table[(d >> 6) & 0X3F]);
+ writer.write(table[d & 0X3F]);
+
+ if (chunked && ++charCount == 19) {
+ writer.write(CHUNK_SEPARATOR);
+ charCount = 0;
+ }
+ }
+
+ if (dLimit != data.length) {
+ int d = (data[dLimit] & 0XFF) << 16;
+
+ if (dLimit + 1 != data.length) {
+ d |= (data[dLimit + 1] & 0XFF) << 8;
+ }
+
+ writer.write(table[d >> 18]);
+ writer.write(table[(d >> 12) & 0X3F]);
+ if (pad) {
+ writer.write((dLimit + 1 < data.length) ? table[(d >> 6) & 0X3F] : '=');
+ writer.write('=');
+ } else {
+ if (dLimit + 1 < data.length) {
+ writer.write(table[(d >> 6) & 0X3F]);
+ }
+ }
+ if (chunked && charCount != 0) {
+ writer.write(CHUNK_SEPARATOR);
+ }
+ }
+
+ return writer;
+ }
+
+ public String toString() {
+ StringWriter buffer = new StringWriter();
+
+ try {
+ writeTo(buffer);
+ } catch (IOException e) {
+ throw new StringWriterIOException(e);
+ }
+
+ return buffer.toString();
+ }
+ };
+ }
+
+ /**
+ * Produce a Writable object which writes the Base64 encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 encoding and chunking see <code>RFC 4648</code>.
+ *
+ * @param data byte array to be encoded
+ * @return object which will write the Base64 encoding of the byte array
+ * @since 1.0
+ */
+ public static Writable encodeBase64(final byte[] data) {
+ return encodeBase64(data, false);
+ }
+
+ /**
+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
+ * <p>
+ * The method omits padding and is equivalent to calling
+ * {@link org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(Byte[], boolean)} with a
+ * value of {@code false}.
+ *
+ * @param data Byte array to be encoded
+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
+ * @see org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(Byte[], boolean)
+ * @since 2.5.0
+ */
+ public static Writable encodeBase64Url(Byte[] data) {
+ return encodeBase64Url(data, false);
+ }
+
+ /**
+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
+ *
+ * @param data Byte array to be encoded
+ * @param pad whether or not the encoded data should be padded
+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
+ * @since 2.5.0
+ */
+ public static Writable encodeBase64Url(Byte[] data, boolean pad) {
+ return encodeBase64Url(DefaultTypeTransformation.convertToByteArray(data), pad);
+ }
+
+ /**
+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
+ * <p>
+ * The method omits padding and is equivalent to calling
+ * {@link org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(byte[], boolean)} with a
+ * value of {@code false}.
+ *
+ * @param data Byte array to be encoded
+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
+ * @see org.codehaus.groovy.runtime.EncodingGroovyMethods#encodeBase64Url(byte[], boolean)
+ * @since 2.5.0
+ */
+ public static Writable encodeBase64Url(final byte[] data) {
+ return encodeBase64Url(data, false);
+ }
+
+ /**
+ * Produce a Writable object which writes the Base64 URL and Filename Safe encoding of the byte array.
+ * Calling toString() on the result returns the encoding as a String. For more
+ * information on Base64 URL and Filename Safe encoding see <code>RFC 4648 - Section 5
+ * Base 64 Encoding with URL and Filename Safe Alphabet</code>.
+ *
+ * @param data Byte array to be encoded
+ * @param pad whether or not the encoded data should be padded
+ * @return object which will write the Base64 URL and Filename Safe encoding of the byte array
+ * @since 2.5.0
+ */
+ public static Writable encodeBase64Url(final byte[] data, final boolean pad) {
+ return encodeBase64(data, false, true, pad);
+ }
+
+ /**
+ * Decode the String from Base64 into a byte array.
+ *
+ * @param value the string to be decoded
+ * @return the decoded bytes as an array
+ * @since 1.0
+ */
+ public static byte[] decodeBase64(String value) {
+ return decodeBase64(value, false);
+ }
+
+ /**
+ * Decodes a Base64 URL and Filename Safe encoded String into a byte array.
+ *
+ * @param value the string to be decoded
+ * @return the decoded bytes as an array
+ * @since 2.5.0
+ */
+ public static byte[] decodeBase64Url(String value) {
+ return decodeBase64(value, true);
+ }
+
+ private static byte[] decodeBase64(String value, boolean urlSafe) {
+ int byteShift = 4;
+ int tmp = 0;
+ boolean done = false;
+ final StringBuilder buffer = new StringBuilder();
+ final byte[] table = urlSafe ? TRANSLATE_TABLE_URLSAFE : TRANSLATE_TABLE;
+ for (int i = 0; i != value.length(); i++) {
+ final char c = value.charAt(i);
+ final int sixBit = (c < 123) ? table[c] : 66;
+
+ if (sixBit < 64) {
+ if (done)
+ throw new RuntimeException("= character not at end of base64 value"); // TODO: change this exception type
+
+ tmp = (tmp << 6) | sixBit;
+
+ if (byteShift-- != 4) {
+ buffer.append((char) ((tmp >> (byteShift * 2)) & 0XFF));
+ }
+
+ } else if (sixBit == 64) {
+
+ byteShift--;
+ done = true;
+
+ } else if (sixBit == 66) {
+ // RFC 2045 says that I'm allowed to take the presence of
+ // these characters as evidence of data corruption
+ // So I will
+ throw new RuntimeException("bad character in base64 value"); // TODO: change this exception type
+ }
+
+ if (byteShift == 0) byteShift = 4;
+ }
+
+ try {
+ return buffer.toString().getBytes("ISO-8859-1");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("Base 64 decode produced byte values > 255"); // TODO: change this exception type
+ }
+ }
+
+ /**
+ * Produces a Writable that writes the hex encoding of the Byte[]. Calling
+ * toString() on this Writable returns the hex encoding as a String. The hex
+ * encoding includes two characters for each byte and all letters are lower case.
+ *
+ * @param data byte array to be encoded
+ * @return object which will write the hex encoding of the byte array
+ * @see Integer#toHexString(int)
+ */
+ public static Writable encodeHex(final Byte[] data) {
+ return encodeHex(DefaultTypeTransformation.convertToByteArray(data));
+ }
+
+ /**
+ * Produces a Writable that writes the hex encoding of the byte[]. Calling
+ * toString() on this Writable returns the hex encoding as a String. The hex
+ * encoding includes two characters for each byte and all letters are lower case.
+ *
+ * @param data byte array to be encoded
+ * @return object which will write the hex encoding of the byte array
+ * @see Integer#toHexString(int)
+ */
+ public static Writable encodeHex(final byte[] data) {
+ return new Writable() {
+ public Writer writeTo(Writer out) throws IOException {
+ for (int i = 0; i < data.length; i++) {
+ // convert byte into unsigned hex string
+ String hexString = Integer.toHexString(data[i] & 0xFF);
+
+ // add leading zero if the length of the string is one
+ if (hexString.length() < 2) {
+ out.write("0");
+ }
+
+ // write hex string to writer
+ out.write(hexString);
+ }
+ return out;
+ }
+
+ public String toString() {
+ StringWriter buffer = new StringWriter();
+
+ try {
+ writeTo(buffer);
+ } catch (IOException e) {
+ throw new StringWriterIOException(e);
+ }
+
+ return buffer.toString();
+ }
+ };
+ }
+
+ /**
+ * Decodes a hex string to a byte array. The hex string can contain either upper
+ * case or lower case letters.
+ *
+ * @param value string to be decoded
+ * @return decoded byte array
+ * @throws NumberFormatException If the string contains an odd number of characters
+ * or if the characters are not valid hexadecimal values.
+ */
+ public static byte[] decodeHex(final String value) {
+ // if string length is odd then throw exception
+ if (value.length() % 2 != 0) {
+ throw new NumberFormatException("odd number of characters in hex string");
+ }
+
+ byte[] bytes = new byte[value.length() / 2];
+ for (int i = 0; i < value.length(); i += 2) {
+ bytes[i / 2] = (byte) Integer.parseInt(value.substring(i, i + 2), 16);
+ }
+
+ return bytes;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethodsSupport.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethodsSupport.java b/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethodsSupport.java
new file mode 100644
index 0000000..24929a8
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/EncodingGroovyMethodsSupport.java
@@ -0,0 +1,90 @@
+/*
+ * 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;
+
+/**
+ * Keep this constant in a separate file as it is troublesome for Antlr to parse for doc purposes.
+ */
+public class EncodingGroovyMethodsSupport {
+ static final byte[] TRANSLATE_TABLE = (
+ "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ // \t \n \r
+ + "\u0042\u0041\u0041\u0042\u0042\u0041\u0042\u0042"
+ //
+ + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ //
+ + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ // sp ! " # $ % & '
+ + "\u0041\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ // ( ) * + , - . /
+ + "\u0042\u0042\u0042\u003E\u0042\u0042\u0042\u003F"
+ // 0 1 2 3 4 5 6 7
+ + "\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B"
+ // 8 9 : ; < = > ?
+ + "\u003C\u003D\u0042\u0042\u0042\u0040\u0042\u0042"
+ // @ A B C D E F G
+ + "\u0042\u0000\u0001\u0002\u0003\u0004\u0005\u0006"
+ // H I J K L M N O
+ + "\u0007\u0008\t\n\u000B\u000C\r\u000E"
+ // P Q R S T U V W
+ + "\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016"
+ // X Y Z [ \ ] ^ _
+ + "\u0017\u0018\u0019\u0042\u0042\u0042\u0042\u0042"
+ // ' a b c d e f g
+ + "\u0042\u001A\u001B\u001C\u001D\u001E\u001F\u0020"
+ // h i j k l m n o
+ + "\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028"
+ // p q r s t u v w
+ + "\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030"
+ // x y z
+ + "\u0031\u0032\u0033").getBytes();
+
+ static final byte[] TRANSLATE_TABLE_URLSAFE = (
+ "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ // \t \n \r
+ + "\u0042\u0041\u0041\u0042\u0042\u0041\u0042\u0042"
+ //
+ + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ //
+ + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ // sp ! " # $ % & '
+ + "\u0041\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+ // ( ) * + , - . /
+ + "\u0042\u0042\u0042\u0042\u0042\u003E\u0042\u0042"
+ // 0 1 2 3 4 5 6 7
+ + "\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B"
+ // 8 9 : ; < = > ?
+ + "\u003C\u003D\u0042\u0042\u0042\u0040\u0042\u0042"
+ // @ A B C D E F G
+ + "\u0042\u0000\u0001\u0002\u0003\u0004\u0005\u0006"
+ // H I J K L M N O
+ + "\u0007\u0008\t\n\u000B\u000C\r\u000E"
+ // P Q R S T U V W
+ + "\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016"
+ // X Y Z [ \ ] ^ _
+ + "\u0017\u0018\u0019\u0042\u0042\u0042\u0042\u003F"
+ // ' a b c d e f g
+ + "\u0042\u001A\u001B\u001C\u001D\u001E\u001F\u0020"
+ // h i j k l m n o
+ + "\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028"
+ // p q r s t u v w
+ + "\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030"
+ // x y z
+ + "\u0031\u0032\u0033").getBytes();
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/FlushingStreamWriter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/FlushingStreamWriter.java b/src/main/java/org/codehaus/groovy/runtime/FlushingStreamWriter.java
new file mode 100644
index 0000000..6fb77b7
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/FlushingStreamWriter.java
@@ -0,0 +1,50 @@
+/*
+ * 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 java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+/**
+ * Stream writer which flushes after each write operation.
+ *
+ * @author Guillaume Laforge
+ */
+public class FlushingStreamWriter extends OutputStreamWriter {
+
+ public FlushingStreamWriter(OutputStream out) {
+ super(out);
+ }
+
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ super.write(cbuf, off, len);
+ flush();
+ }
+
+ public void write(int c) throws IOException {
+ super.write(c);
+ flush();
+ }
+
+ public void write(String str, int off, int len) throws IOException {
+ super.write(str, off, len);
+ flush();
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/GStringImpl.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/GStringImpl.java b/src/main/java/org/codehaus/groovy/runtime/GStringImpl.java
new file mode 100644
index 0000000..d4cd857
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/GStringImpl.java
@@ -0,0 +1,64 @@
+/*
+ * 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.GString;
+
+/**
+ * Default implementation of a GString used by the compiler. A GString
+ * consist of a list of values and strings which can be combined to
+ * create a new String.
+ *
+ * @author Jochen Theodorou
+ * @see groovy.lang.GString
+ */
+public class GStringImpl extends GString {
+ private final String[] strings;
+
+ /**
+ * Create a new GString with values and strings.
+ * <p>
+ * Each value is prefixed by a string, after the last value
+ * an additional String might be used. This means
+ * <code>strings.length == values.length || strings.length == values.length + 1</code>.
+ * <p>
+ * <b>NOTE:</b> The lengths are <b>not</b> checked. Using different lengths might result
+ * in unpredictable behaviour.
+ *
+ * @param values the value parts
+ * @param strings the string parts
+ */
+ public GStringImpl(Object[] values, String[] strings) {
+ super(values);
+ this.strings = strings;
+ }
+
+ /**
+ * Get the strings of this GString.
+ * <p>
+ * This methods returns the same array as used in the constructor. Changing
+ * the values will result in changes of the GString. It is not recommended
+ * to do so.
+ */
+ public String[] getStrings() {
+ return strings;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/GeneratedClosure.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/GeneratedClosure.java b/src/main/java/org/codehaus/groovy/runtime/GeneratedClosure.java
new file mode 100644
index 0000000..50da843
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/GeneratedClosure.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Marker interface to identify closures generated by the groovy compiler.
+ * For internal use only!
+ *
+ * @author Jochen Theodorou
+ * @since 1.5
+ * @see org.codehaus.groovy.runtime.metaclass.ClosureMetaClass
+ */
+public interface GeneratedClosure {}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/GroovyCategorySupport.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/GroovyCategorySupport.java b/src/main/java/org/codehaus/groovy/runtime/GroovyCategorySupport.java
new file mode 100644
index 0000000..19f53d2
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/GroovyCategorySupport.java
@@ -0,0 +1,365 @@
+/*
+ * 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 org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.runtime.metaclass.DefaultMetaClassInfo;
+import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
+import org.codehaus.groovy.vmplugin.VMPluginFactory;
+
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author sam
+ * @author Paul King
+ * @author Alex Tkachman
+ */
+public class GroovyCategorySupport {
+
+ private static int categoriesInUse = 0;
+
+ public static class CategoryMethodList extends ArrayList<CategoryMethod> {
+ public final int level;
+ final CategoryMethodList previous;
+ final AtomicInteger usage;
+
+ public CategoryMethodList(String name, int level, CategoryMethodList previous) {
+ this.level = level;
+ this.previous = previous;
+ if (previous != null) {
+ addAll(previous);
+ usage = previous.usage;
+ }
+ else {
+ usage = getCategoryNameUsage (name);
+ }
+ }
+
+ public boolean add(CategoryMethod o) {
+ usage.incrementAndGet();
+ return super.add(o);
+ }
+ }
+
+ public static class ThreadCategoryInfo extends HashMap<String, CategoryMethodList>{
+
+ private static final Object LOCK = new Object();
+
+ int level;
+
+ private Map<String, String> propertyGetterMap;
+ private Map<String, String> propertySetterMap;
+
+ private void newScope () {
+ synchronized (LOCK) {
+ categoriesInUse++;
+ DefaultMetaClassInfo.setCategoryUsed(true);
+ }
+ VMPluginFactory.getPlugin().invalidateCallSites();
+ level++;
+ }
+
+ private void endScope () {
+ for (Iterator<Map.Entry<String, CategoryMethodList>> it = entrySet().iterator(); it.hasNext(); ) {
+ final Map.Entry<String, CategoryMethodList> e = it.next();
+ final CategoryMethodList list = e.getValue();
+ if (list.level == level) {
+ final CategoryMethodList prev = list.previous;
+ if (prev == null) {
+ it.remove();
+ list.usage.addAndGet(-list.size());
+ }
+ else {
+ e.setValue(prev);
+ list.usage.addAndGet(prev.size()-list.size());
+ }
+ }
+ }
+ level--;
+ VMPluginFactory.getPlugin().invalidateCallSites();
+ synchronized (LOCK) {
+ if (--categoriesInUse == 0) {
+ DefaultMetaClassInfo.setCategoryUsed(false);
+ }
+ }
+ if (level == 0) {
+ THREAD_INFO.remove();
+ }
+ }
+
+ private <T> T use(Class categoryClass, Closure<T> closure) {
+ newScope();
+ try {
+ use(categoryClass);
+ return closure.call();
+ } finally {
+ endScope();
+ }
+ }
+
+ public <T> T use(List<Class> categoryClasses, Closure<T> closure) {
+ newScope();
+ try {
+ for (Class categoryClass : categoryClasses) {
+ use(categoryClass);
+ }
+ return closure.call();
+ } finally {
+ endScope();
+ }
+ }
+
+ private void applyUse(CachedClass cachedClass) {
+ CachedMethod[] methods = cachedClass.getMethods();
+ for (CachedMethod cachedMethod : methods) {
+ if (cachedMethod.isStatic() && cachedMethod.isPublic()) {
+ CachedClass[] paramTypes = cachedMethod.getParameterTypes();
+ if (paramTypes.length > 0) {
+ CachedClass metaClass = paramTypes[0];
+ CategoryMethod mmethod = new CategoryMethod(cachedMethod, metaClass.getTheClass());
+ final String name = cachedMethod.getName();
+ CategoryMethodList list = get(name);
+ if (list == null || list.level != level) {
+ list = new CategoryMethodList(name, level, list);
+ put(name, list);
+ }
+ list.add(mmethod);
+ Collections.sort(list);
+ cachePropertyAccessor(mmethod);
+ }
+ }
+ }
+ }
+
+ private void cachePropertyAccessor(CategoryMethod method) {
+ String name = method.getName();
+ int parameterLength = method.getParameterTypes().length;
+
+ if (name.startsWith("get") && name.length() > 3 && parameterLength == 0) {
+ propertyGetterMap = putPropertyAccessor(3, name, propertyGetterMap);
+ }
+ else if (name.startsWith("set") && name.length() > 3 && parameterLength == 1) {
+ propertySetterMap = putPropertyAccessor(3, name, propertySetterMap);
+ }
+ }
+
+ // Precondition: accessorName.length() > prefixLength
+ private Map<String, String> putPropertyAccessor(int prefixLength, String accessorName, Map<String, String> map) {
+ if (map == null) {
+ map = new HashMap<String, String>();
+ }
+ String property = accessorName.substring(prefixLength, prefixLength+1).toLowerCase() + accessorName.substring(prefixLength+1);
+ map.put(property, accessorName);
+ return map;
+ }
+
+ private void use(Class categoryClass) {
+ CachedClass cachedClass = ReflectionCache.getCachedClass(categoryClass);
+ LinkedList<CachedClass> classStack = new LinkedList<CachedClass>();
+ for (CachedClass superClass = cachedClass; superClass.getTheClass()!=Object.class; superClass = superClass.getCachedSuperClass()) {
+ classStack.add(superClass);
+ }
+
+ while (!classStack.isEmpty()) {
+ CachedClass klazz = classStack.removeLast();
+ applyUse(klazz);
+ }
+ }
+
+ public CategoryMethodList getCategoryMethods(String name) {
+ return level == 0 ? null : get(name);
+ }
+
+
+ String getPropertyCategoryGetterName(String propertyName){
+ return propertyGetterMap != null ? propertyGetterMap.get(propertyName) : null;
+ }
+
+ String getPropertyCategorySetterName(String propertyName){
+ return propertySetterMap != null ? propertySetterMap.get(propertyName) : null;
+ }
+ }
+
+ private static final MyThreadLocal THREAD_INFO = new MyThreadLocal();
+
+ public static class CategoryMethod extends NewInstanceMetaMethod implements Comparable {
+ private final Class metaClass;
+
+ public CategoryMethod(CachedMethod metaMethod, Class metaClass) {
+ super(metaMethod);
+ this.metaClass = metaClass;
+ }
+
+ public boolean isCacheable() { return false; }
+
+ /**
+ * Sort by most specific to least specific.
+ *
+ * @param o the object to compare against
+ */
+ public int compareTo(Object o) {
+ CategoryMethod thatMethod = (CategoryMethod) o;
+ Class thisClass = metaClass;
+ Class thatClass = thatMethod.metaClass;
+ if (thisClass == thatClass) return 0;
+ if (isChildOfParent(thisClass, thatClass)) return -1;
+ if (isChildOfParent(thatClass, thisClass)) return 1;
+ return 0;
+ }
+
+ private boolean isChildOfParent(Class candidateChild, Class candidateParent) {
+ Class loop = candidateChild;
+ while(loop != null && loop != Object.class) {
+ loop = loop.getSuperclass();
+ if (loop == candidateParent) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public static AtomicInteger getCategoryNameUsage (String name) {
+ return THREAD_INFO.getUsage (name);
+ }
+
+ /**
+ * Create a scope based on given categoryClass and invoke closure within that scope.
+ *
+ * @param categoryClass the class containing category methods
+ * @param closure the closure during which to make the category class methods available
+ * @return the value returned from the closure
+ */
+ public static <T> T use(Class categoryClass, Closure<T> closure) {
+ return THREAD_INFO.getInfo().use(categoryClass, closure);
+ }
+
+ /**
+ * Create a scope based on given categoryClasses and invoke closure within that scope.
+ *
+ * @param categoryClasses the list of classes containing category methods
+ * @param closure the closure during which to make the category class methods available
+ * @return the value returned from the closure
+ */
+ public static <T> T use(List<Class> categoryClasses, Closure<T> closure) {
+ return THREAD_INFO.getInfo().use(categoryClasses, closure);
+ }
+
+ public static boolean hasCategoryInCurrentThread() {
+ /*
+ * Synchronization is avoided here for performance reasons since
+ * this method is called frequently from callsite locations. For
+ * a typical case when no Categories are in use the initialized
+ * value of 0 will be correctly read. For cases where multiple
+ * Threads are using Categories it is possible that a stale
+ * non-zero value may be read but in that case the ThreadLocal
+ * check will produce the correct result. When the current Thread
+ * is using Categories, it would have incremented the counter
+ * so whatever version of the value it observes here should be
+ * non-zero and good enough for the purposes of this quick exit
+ * check.
+ */
+ if (categoriesInUse == 0) {
+ return false;
+ }
+ ThreadCategoryInfo infoNullable = THREAD_INFO.getInfoNullable();
+ return infoNullable != null && infoNullable.level != 0;
+ }
+
+ /**
+ * @deprecated use {@link #hasCategoryInCurrentThread()}
+ */
+ @Deprecated
+ public static boolean hasCategoryInAnyThread() {
+ synchronized (ThreadCategoryInfo.LOCK) {
+ return categoriesInUse != 0;
+ }
+ }
+
+ /**
+ * This method is used to pull all the new methods out of the local thread context with a particular name.
+ *
+ * @param name the method name of interest
+ * @return the list of methods
+ */
+ public static CategoryMethodList getCategoryMethods(String name) {
+ final ThreadCategoryInfo categoryInfo = THREAD_INFO.getInfoNullable();
+ return categoryInfo == null ? null : categoryInfo.getCategoryMethods(name);
+ }
+
+ public static String getPropertyCategoryGetterName(String propertyName) {
+ final ThreadCategoryInfo categoryInfo = THREAD_INFO.getInfoNullable();
+ return categoryInfo == null ? null : categoryInfo.getPropertyCategoryGetterName(propertyName);
+ }
+
+ public static String getPropertyCategorySetterName(String propertyName) {
+ final ThreadCategoryInfo categoryInfo = THREAD_INFO.getInfoNullable();
+ return categoryInfo == null ? null : categoryInfo.getPropertyCategorySetterName(propertyName);
+ }
+
+ private static class MyThreadLocal extends ThreadLocal<SoftReference> {
+
+ final ConcurrentHashMap<String,AtomicInteger> usage = new ConcurrentHashMap<String,AtomicInteger> ();
+
+ public ThreadCategoryInfo getInfo() {
+ final SoftReference reference = get();
+ ThreadCategoryInfo tcinfo;
+ if (reference != null) {
+ tcinfo = (ThreadCategoryInfo) reference.get();
+ if( tcinfo == null ) {
+ tcinfo = new ThreadCategoryInfo();
+ set(new SoftReference(tcinfo));
+ }
+ }
+ else {
+ tcinfo = new ThreadCategoryInfo();
+ set(new SoftReference(tcinfo));
+ }
+ return tcinfo;
+ }
+
+ public ThreadCategoryInfo getInfoNullable() {
+ final SoftReference reference = get();
+ return reference == null ? null : (ThreadCategoryInfo) reference.get();
+ }
+
+ public AtomicInteger getUsage (String name) {
+ AtomicInteger u = usage.get(name);
+ if (u != null) {
+ return u;
+ }
+
+ final AtomicInteger ai = new AtomicInteger();
+ final AtomicInteger prev = usage.putIfAbsent(name, ai);
+ return prev == null ? ai : prev;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/runtime/HandleMetaClass.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/runtime/HandleMetaClass.java b/src/main/java/org/codehaus/groovy/runtime/HandleMetaClass.java
new file mode 100644
index 0000000..a838c28
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/runtime/HandleMetaClass.java
@@ -0,0 +1,122 @@
+/*
+ * 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.DelegatingMetaClass;
+import groovy.lang.ExpandoMetaClass;
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaBeanProperty;
+import groovy.lang.MetaClass;
+import groovy.lang.MetaMethod;
+
+import java.lang.reflect.Method;
+
+public class HandleMetaClass extends DelegatingMetaClass {
+ private Object object;
+ private static final Object NONE = new Object();
+
+ public HandleMetaClass(MetaClass mc) {
+ this(mc, null);
+ }
+
+ public HandleMetaClass(MetaClass mc, Object obj) {
+ super(mc);
+ if (obj != null) {
+ if (InvokerHelper.getMetaClass(obj.getClass()) == mc || !(mc instanceof ExpandoMetaClass))
+ object = obj; // object has default meta class, so we need to replace it on demand
+ else
+ object = NONE; // object already has per instance meta class
+ }
+ }
+
+ public void initialize() {
+ replaceDelegate();
+ delegate.initialize();
+ }
+
+ public GroovyObject replaceDelegate() {
+ if (object == null) {
+ if (!(delegate instanceof ExpandoMetaClass)) {
+ delegate = new ExpandoMetaClass(delegate.getTheClass(), true, true);
+ delegate.initialize();
+ }
+ DefaultGroovyMethods.setMetaClass(delegate.getTheClass(), delegate);
+ }
+ else {
+ if (object != NONE) {
+ final MetaClass metaClass = delegate;
+ delegate = new ExpandoMetaClass(delegate.getTheClass(), false, true);
+ if (metaClass instanceof ExpandoMetaClass) {
+ ExpandoMetaClass emc = (ExpandoMetaClass) metaClass;
+ for (MetaMethod method : emc.getExpandoMethods())
+ ((ExpandoMetaClass)delegate).registerInstanceMethod(method);
+ }
+ delegate.initialize();
+ MetaClassHelper.doSetMetaClass(object, delegate);
+ object = NONE;
+ }
+ }
+ return (GroovyObject)delegate;
+ }
+
+ public Object invokeMethod(String name, Object args) {
+ return replaceDelegate().invokeMethod(name, args);
+ }
+
+ // this method mimics EMC behavior
+ public Object getProperty(String property) {
+ if(ExpandoMetaClass.isValidExpandoProperty(property)) {
+ if(property.equals(ExpandoMetaClass.STATIC_QUALIFIER) ||
+ property.equals(ExpandoMetaClass.CONSTRUCTOR) ||
+ Holder.META_CLASS.hasProperty(this, property) == null) {
+ return replaceDelegate().getProperty(property);
+ }
+ }
+ return Holder.META_CLASS.getProperty(this, property);
+ }
+
+ public void setProperty(String property, Object newValue) {
+ replaceDelegate().setProperty(property, newValue);
+ }
+
+ public void addNewInstanceMethod(Method method) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addNewStaticMethod(Method method) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addMetaMethod(MetaMethod metaMethod) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addMetaBeanProperty(MetaBeanProperty metaBeanProperty) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean equals(Object obj) {
+ return super.equals(obj) || getAdaptee().equals(obj) || (obj instanceof HandleMetaClass && equals(((HandleMetaClass)obj).getAdaptee()));
+ }
+
+ // Lazily initialize the single instance of the HandleMetaClass metaClass
+ private static class Holder {
+ static final MetaClass META_CLASS = InvokerHelper.getMetaClass(HandleMetaClass.class);
+ }
+}