http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/SpreadListEvaluatingException.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/SpreadListEvaluatingException.java b/src/main/groovy/groovy/lang/SpreadListEvaluatingException.java new file mode 100644 index 0000000..b6de965 --- /dev/null +++ b/src/main/groovy/groovy/lang/SpreadListEvaluatingException.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +public class SpreadListEvaluatingException extends GroovyRuntimeException { + public SpreadListEvaluatingException(String message) { + super(message); + } +}
http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/SpreadMap.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/SpreadMap.java b/src/main/groovy/groovy/lang/SpreadMap.java new file mode 100644 index 0000000..7ba9e4f --- /dev/null +++ b/src/main/groovy/groovy/lang/SpreadMap.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Helper to turn a list with an even number of elements into a Map. + * + * @author Pilho Kim + * @author Tim Tiemens + */ +public class SpreadMap extends HashMap { + private int hashCode; + + public SpreadMap(Object[] values) { + int i = 0; + while (i < values.length) { + super.put(values[i++], values[i++]); + } + } + + public SpreadMap(Map map) { + super(map); + } + + /** + * @since 1.8.0 + * @param list the list to make spreadable + */ + public SpreadMap(List list) { + this(list.toArray()); + } + + public Object put(Object key, Object value) { + throw new RuntimeException("SpreadMap: " + this + " is an immutable map, and so (" + + key + ": " + value + ") cannot be added."); + } + + public Object remove(Object key) { + throw new RuntimeException("SpreadMap: " + this + " is an immutable map, and so the key (" + + key + ") cannot be deleted."); + } + + public void putAll(Map t) { + throw new RuntimeException("SpreadMap: " + this + " is an immutable map, and so the map (" + + t + ") cannot be put in this spreadMap."); + } + + public boolean equals(Object that) { + if (that instanceof SpreadMap) { + return equals((SpreadMap) that); + } + return false; + } + + public boolean equals(SpreadMap that) { + if (that == null) return false; + + if (size() == that.size()) { + for (Object key : keySet()) { + if (!DefaultTypeTransformation.compareEqual(get(key), that.get(key))) { + return false; + } + } + return true; + } + return false; + } + + public int hashCode() { + if (hashCode == 0) { + for (Object key : keySet()) { + int hash = (key != null) ? key.hashCode() : 0xbabe; + hashCode ^= hash; + } + } + return hashCode; + } + + /** + * @return the string expression of <code>this</code> + */ + public String toString() { + if (isEmpty()) { + return "*:[:]"; + } + StringBuilder sb = new StringBuilder("*:["); + Iterator iter = keySet().iterator(); + while (iter.hasNext()) { + Object key = iter.next(); + sb.append(key).append(":").append(get(key)); + if (iter.hasNext()) + sb.append(", "); + } + sb.append("]"); + return sb.toString(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/SpreadMapEvaluatingException.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/SpreadMapEvaluatingException.java b/src/main/groovy/groovy/lang/SpreadMapEvaluatingException.java new file mode 100644 index 0000000..408a223 --- /dev/null +++ b/src/main/groovy/groovy/lang/SpreadMapEvaluatingException.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +public class SpreadMapEvaluatingException extends GroovyRuntimeException { + public SpreadMapEvaluatingException(String message) { + super(message); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/StringWriterIOException.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/StringWriterIOException.java b/src/main/groovy/groovy/lang/StringWriterIOException.java new file mode 100644 index 0000000..0372399 --- /dev/null +++ b/src/main/groovy/groovy/lang/StringWriterIOException.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +import java.io.IOException; + +/** + * An IO exception occurred trying to append to a StringWriter which should never happen. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class StringWriterIOException extends RuntimeException { + + public StringWriterIOException(IOException e) { + super(e); + } + + public IOException getIOException() { + return (IOException) getCause(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/TracingInterceptor.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/TracingInterceptor.java b/src/main/groovy/groovy/lang/TracingInterceptor.java new file mode 100644 index 0000000..f67de11 --- /dev/null +++ b/src/main/groovy/groovy/lang/TracingInterceptor.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +/* + * This {@link Interceptor} traces method calls on the proxied object to a log. + * By default, the log is simply <pre>System.out</pre>; however, that can be + * changed with the <pre>setWriter(Writer)</pre> method. + * <p> + * A message will be written to output before a method is invoked and after a method + * is invoked. If methods are nested, and invoke one another, then indentation + * of two spaces is written. + * <p> + * Here is an example usage on the ArrayList object: <br> + * <pre class="groovyTestCase"> + * def proxy = ProxyMetaClass.getInstance(ArrayList.class) + * proxy.interceptor = new TracingInterceptor() + * proxy.use { + * def list = [1, 2, 3] + * assert 3 == list.size() + * assert list.contains(1) + * } + * </pre> + * Running this code produces this output: + * <pre> + * before java.util.ArrayList.size() + * after java.util.ArrayList.size() + * before java.util.ArrayList.contains(java.lang.Integer) + * after java.util.ArrayList.contains(java.lang.Integer) + * </pre> + */ +public class TracingInterceptor implements Interceptor { + + protected Writer writer = new PrintWriter(System.out); + private int indent = 0; + + /** + * Returns the writer associated with this interceptor. + */ + public Writer getWriter() { + return writer; + } + + /** + * Changes the writer associated with this interceptor. + */ + public void setWriter(Writer writer) { + this.writer = writer; + } + + public Object beforeInvoke(Object object, String methodName, Object[] arguments) { + write(object, methodName, arguments, "before"); + indent++ ; + return null; + } + + public Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) { + indent--; + write(object, methodName, arguments, "after "); + return result; + } + + public boolean doInvoke() { + return true; + } + private String indent(){ + StringBuilder result = new StringBuilder(); + for (int i=0; i<indent;i++){ + result.append(" "); + } + return result.toString(); + } + + protected void write(Object object, String methodName, Object[] arguments, final String origin) { + try { + writer.write(indent()); + writer.write(origin); + writer.write(" "); + Class theClass = object instanceof Class ? (Class) object: object.getClass(); + writeInfo(theClass, methodName, arguments); + writer.write("\n"); + writer.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void writeInfo(final Class aClass, String methodName, Object[] arguments) throws IOException { + writer.write(aClass.getName()); + writer.write("."); + writer.write(methodName); + writer.write("("); + for (int i = 0; i < arguments.length; i++) { + if (i > 0) writer.write(", "); + Object argument = arguments[i]; + writer.write(argument.getClass().getName()); + } + writer.write(")"); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/TrampolineClosure.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/TrampolineClosure.java b/src/main/groovy/groovy/lang/TrampolineClosure.java new file mode 100644 index 0000000..ed68f00 --- /dev/null +++ b/src/main/groovy/groovy/lang/TrampolineClosure.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +/** + * A TrampolineClosure wraps a closure that needs to be executed on a functional trampoline. + * Upon calling, a TrampolineClosure will call the original closure waiting for its result. + * If the outcome of the call is another instance of a TrampolineClosure, created perhaps as a result to a call to the TrampolineClosure.trampoline() + * method, the TrampolineClosure will again be invoked. This repetitive invocation of returned TrampolineClosure instances will continue + * until a value other than TrampolineClosure is returned. + * That value will become the final result of the trampoline. + * + * @author Vaclav Pech + */ +final class TrampolineClosure<V> extends Closure<V> { + + private final Closure<V> original; + + TrampolineClosure(final Closure<V> original) { + super(original.getOwner(), original.getDelegate()); + this.original = original; + } + + /** + * Delegates to the wrapped closure + */ + @Override + public int getMaximumNumberOfParameters() { + return original.maximumNumberOfParameters; + } + + /** + * Delegates to the wrapped closure + */ + @Override + public Class[] getParameterTypes() { + return original.parameterTypes; + } + + /** + * Starts the trampoline loop and calls the wrapped closure as the first step. + * @return The final result of the trampoline + */ + @Override + public V call() { + return loop(original.call()); + } + + /** + * Starts the trampoline loop and calls the wrapped closure as the first step. + * @return The final result of the trampoline + */ + @Override + public V call(final Object arguments) { + return loop(original.call(arguments)); + } + + /** + * Starts the trampoline loop and calls the wrapped closure as the first step. + * @return The final result of the trampoline + */ + @Override + public V call(final Object... args) { + return loop(original.call(args)); + } + + private V loop(final Object lastResult) { + Object result = lastResult; + + for (;;) { + if (result instanceof TrampolineClosure) { + result = ((TrampolineClosure)result).original.call(); + } else return (V) result; + } + } + + /** + * Builds a trampolined variant of the current closure. + * @param args Parameters to curry to the underlying closure. + * @return An instance of TrampolineClosure wrapping the original closure after currying. + */ + @Override + public Closure<V> trampoline(final Object... args) { + return new TrampolineClosure<V>(original.curry(args)); + } + + /** + * Returns itself, since it is a good enough trampolined variant of the current closure. + * @return An instance of TrampolineClosure wrapping the original closure. + */ + @Override + public Closure<V> trampoline() { + return this; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple.java b/src/main/groovy/groovy/lang/Tuple.java new file mode 100644 index 0000000..036d13e --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; + +import java.io.Serializable; +import java.util.AbstractList; +import java.util.List; +import java.util.Objects; + +/** + * Represents a list of Objects. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class Tuple<E> extends AbstractList<E> implements Serializable { + private static final long serialVersionUID = -6707770506387821031L; + private final E[] contents; + + public Tuple(E... contents) { + if (contents == null) throw new NullPointerException(); + this.contents = contents; + } + + @Override + public E get(int index) { + return contents[index]; + } + + @Override + public int size() { + return contents.length; + } + + @SuppressWarnings("unchecked") + @Override + public List<E> subList(int fromIndex, int toIndex) { + int size = toIndex - fromIndex; + E[] newContent = (E[]) new Object[size]; + System.arraycopy(contents, fromIndex, newContent, 0, size); + return new Tuple<>(newContent); + } + + public Tuple<E> subTuple(int fromIndex, int toIndex) { + return (Tuple<E>) subList(fromIndex, toIndex); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || !(o instanceof Tuple)) return false; + + Tuple that = (Tuple) o; + int size = size(); + if (size != that.size()) return false; + for (int i = 0; i < size; i++) { + if (!DefaultTypeTransformation.compareEqual(get(i), that.get(i))) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash(contents); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple1.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple1.java b/src/main/groovy/groovy/lang/Tuple1.java new file mode 100644 index 0000000..9b8d6bd --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple1.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +/** + * Represents a list of 1 typed Object. + * + * @since 2.5.0 + */ +public class Tuple1<T1> extends Tuple { + private static final long serialVersionUID = -4647790147461409603L; + private final T1 first; + + public Tuple1(T1 first) { + super(first); + + this.first = first; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 1; + } + + public T1 getFirst() { + return first; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple2.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple2.java b/src/main/groovy/groovy/lang/Tuple2.java new file mode 100644 index 0000000..b096aee --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple2.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +/** + * Represents a list of 2 typed Objects. + */ +public class Tuple2<T1, T2> extends Tuple { + private static final long serialVersionUID = 9006144674906325597L; + private final T1 first; + private final T2 second; + + public Tuple2(T1 first, T2 second) { + super(first, second); + + this.first = first; + this.second = second; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 2; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple3.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple3.java b/src/main/groovy/groovy/lang/Tuple3.java new file mode 100644 index 0000000..b5fe7d9 --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple3.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.lang; + +/** + * Represents a list of 3 typed Objects. + * + * @since 2.5.0 + */ +public class Tuple3<T1, T2, T3> extends Tuple { + private static final long serialVersionUID = 8469774237154310687L; + private final T1 first; + private final T2 second; + private final T3 third; + + public Tuple3(T1 first, T2 second, T3 third) { + super(first, second, third); + + this.first = first; + this.second = second; + this.third = third; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + case 2: + return third; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 3; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + public T3 getThird() { + return third; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple4.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple4.java b/src/main/groovy/groovy/lang/Tuple4.java new file mode 100644 index 0000000..04f414e --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple4.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.lang; + +/** + * Represents a list of 4 typed Objects. + * + * @since 2.5.0 + */ +public class Tuple4<T1, T2, T3, T4> extends Tuple { + private static final long serialVersionUID = -7788878731471377207L; + private final T1 first; + private final T2 second; + private final T3 third; + private final T4 fourth; + + public Tuple4(T1 first, T2 second, T3 third, T4 fourth) { + super(first, second, third, fourth); + + this.first = first; + this.second = second; + this.third = third; + this.fourth = fourth; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + case 2: + return third; + case 3: + return fourth; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 4; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + public T3 getThird() { + return third; + } + + public T4 getFourth() { + return fourth; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple5.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple5.java b/src/main/groovy/groovy/lang/Tuple5.java new file mode 100644 index 0000000..ee9c802 --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple5.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.lang; + +/** + * Represents a list of 5 typed Objects. + * + * @since 2.5.0 + */ +public class Tuple5<T1, T2, T3, T4, T5> extends Tuple { + private static final long serialVersionUID = 6722094358774027115L; + private final T1 first; + private final T2 second; + private final T3 third; + private final T4 fourth; + private final T5 fifth; + + public Tuple5(T1 first, T2 second, T3 third, T4 fourth, T5 fifth) { + super(first, second, third, fourth, fifth); + + this.first = first; + this.second = second; + this.third = third; + this.fourth = fourth; + this.fifth = fifth; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + case 2: + return third; + case 3: + return fourth; + case 4: + return fifth; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 5; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + public T3 getThird() { + return third; + } + + public T4 getFourth() { + return fourth; + } + + public T5 getFifth() { + return fifth; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple6.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple6.java b/src/main/groovy/groovy/lang/Tuple6.java new file mode 100644 index 0000000..76d12ca --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple6.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.lang; + +/** + * Represents a list of 6 typed Objects. + * + * @since 2.5.0 + */ +public class Tuple6<T1, T2, T3, T4, T5, T6> extends Tuple { + private static final long serialVersionUID = -7848588473093102288L; + private final T1 first; + private final T2 second; + private final T3 third; + private final T4 fourth; + private final T5 fifth; + private final T6 sixth; + + public Tuple6(T1 first, T2 second, T3 third, T4 fourth, T5 fifth, T6 sixth) { + super(first, second, third, fourth, fifth, sixth); + + this.first = first; + this.second = second; + this.third = third; + this.fourth = fourth; + this.fifth = fifth; + this.sixth = sixth; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + case 2: + return third; + case 3: + return fourth; + case 4: + return fifth; + case 5: + return sixth; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 6; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + public T3 getThird() { + return third; + } + + public T4 getFourth() { + return fourth; + } + + public T5 getFifth() { + return fifth; + } + + public T6 getSixth() { + return sixth; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple7.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple7.java b/src/main/groovy/groovy/lang/Tuple7.java new file mode 100644 index 0000000..05046e7 --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple7.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.lang; + +/** + * Represents a list of 7 typed Objects. + * + * @since 2.5.0 + */ +public class Tuple7<T1, T2, T3, T4, T5, T6, T7> extends Tuple { + private static final long serialVersionUID = 4226144828786865766L; + private final T1 first; + private final T2 second; + private final T3 third; + private final T4 fourth; + private final T5 fifth; + private final T6 sixth; + private final T7 seventh; + + public Tuple7(T1 first, T2 second, T3 third, T4 fourth, T5 fifth, T6 sixth, T7 seventh) { + super(first, second, third, fourth, fifth, sixth, seventh); + + this.first = first; + this.second = second; + this.third = third; + this.fourth = fourth; + this.fifth = fifth; + this.sixth = sixth; + this.seventh = seventh; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + case 2: + return third; + case 3: + return fourth; + case 4: + return fifth; + case 5: + return sixth; + case 6: + return seventh; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 7; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + public T3 getThird() { + return third; + } + + public T4 getFourth() { + return fourth; + } + + public T5 getFifth() { + return fifth; + } + + public T6 getSixth() { + return sixth; + } + + public T7 getSeventh() { + return seventh; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple8.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple8.java b/src/main/groovy/groovy/lang/Tuple8.java new file mode 100644 index 0000000..1f38ca9 --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple8.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package groovy.lang; + +/** + * Represents a list of 8 typed Objects. + * + * @since 2.5.0 + */ +public class Tuple8<T1, T2, T3, T4, T5, T6, T7, T8> extends Tuple { + private static final long serialVersionUID = -8895822084644138566L; + private final T1 first; + private final T2 second; + private final T3 third; + private final T4 fourth; + private final T5 fifth; + private final T6 sixth; + private final T7 seventh; + private final T8 eighth; + + public Tuple8(T1 first, T2 second, T3 third, T4 fourth, T5 fifth, T6 sixth, T7 seventh, T8 eighth) { + super(first, second, third, fourth, fifth, sixth, seventh, eighth); + + this.first = first; + this.second = second; + this.third = third; + this.fourth = fourth; + this.fifth = fifth; + this.sixth = sixth; + this.seventh = seventh; + this.eighth = eighth; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + case 2: + return third; + case 3: + return fourth; + case 4: + return fifth; + case 5: + return sixth; + case 6: + return seventh; + case 7: + return eighth; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 8; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + public T3 getThird() { + return third; + } + + public T4 getFourth() { + return fourth; + } + + public T5 getFifth() { + return fifth; + } + + public T6 getSixth() { + return sixth; + } + + public T7 getSeventh() { + return seventh; + } + + public T8 getEighth() { + return eighth; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Tuple9.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Tuple9.java b/src/main/groovy/groovy/lang/Tuple9.java new file mode 100644 index 0000000..3189c10 --- /dev/null +++ b/src/main/groovy/groovy/lang/Tuple9.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +package groovy.lang; + +/** + * Represents a list of 9 typed Objects. + * + * @since 2.5.0 + */ +public class Tuple9<T1, T2, T3, T4, T5, T6, T7, T8, T9> extends Tuple { + private static final long serialVersionUID = -5181196675351911769L; + private final T1 first; + private final T2 second; + private final T3 third; + private final T4 fourth; + private final T5 fifth; + private final T6 sixth; + private final T7 seventh; + private final T8 eighth; + private final T9 ninth; + + public Tuple9(T1 first, T2 second, T3 third, T4 fourth, T5 fifth, T6 sixth, T7 seventh, T8 eighth, T9 ninth) { + super(first, second, third, fourth, fifth, sixth, seventh, eighth, ninth); + + this.first = first; + this.second = second; + this.third = third; + this.fourth = fourth; + this.fifth = fifth; + this.sixth = sixth; + this.seventh = seventh; + this.eighth = eighth; + this.ninth = ninth; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: + return first; + case 1: + return second; + case 2: + return third; + case 3: + return fourth; + case 4: + return fifth; + case 5: + return sixth; + case 6: + return seventh; + case 7: + return eighth; + case 8: + return ninth; + default: + throw new IndexOutOfBoundsException("index: " + index); + } + } + + @Override + public int size() { + return 9; + } + + public T1 getFirst() { + return first; + } + + public T2 getSecond() { + return second; + } + + public T3 getThird() { + return third; + } + + public T4 getFourth() { + return fourth; + } + + public T5 getFifth() { + return fifth; + } + + public T6 getSixth() { + return sixth; + } + + public T7 getSeventh() { + return seventh; + } + + public T8 getEighth() { + return eighth; + } + + public T9 getNinth() { + return ninth; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/Writable.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/Writable.java b/src/main/groovy/groovy/lang/Writable.java new file mode 100644 index 0000000..ec135cd --- /dev/null +++ b/src/main/groovy/groovy/lang/Writable.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang; + +import java.io.IOException; +import java.io.Writer; + + +/** + * Represents an object which is capable of writing itself to a text stream + * in a more efficient format than just creating a toString() representation + * of itself. This mechanism is particularly useful for templates and such like. + * <p> + * It is worth noting that writable implementations often override their + * toString() implementation as well to allow rendering the same result + * directly to a String; however this is not required. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public interface Writable { + + /** + * Writes this object to the given writer. + * <p> + * This is used to defer content creation until the point when it is + * streamed to the output destination. Oftentimes, content will be defined + * but not necessarily created (as is may be the case with a Closure + * definition.) In that case, the output is then 'deferred' to the point + * when it is serialized to the writer. This class may be used whenever an + * object should be responsible for creating its own textual representation, + * but creating the entire output as a single String would be inefficient + * (such as outputting a multi-gigabyte XML document.) + * + * @param out the Writer to which this Writable should output its data. + * @return the Writer that was passed + * @throws IOException if an error occurred while outputting data to the writer + */ + Writer writeTo(Writer out) throws IOException; + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/groovydoc/Groovydoc.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/groovydoc/Groovydoc.java b/src/main/groovy/groovy/lang/groovydoc/Groovydoc.java new file mode 100644 index 0000000..d7d4204 --- /dev/null +++ b/src/main/groovy/groovy/lang/groovydoc/Groovydoc.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang.groovydoc; + +import java.lang.reflect.AnnotatedElement; +import java.util.List; +import java.util.Objects; + +/** + * Represents groovydoc + */ +public class Groovydoc { + private final String content; + private List<GroovydocTag> tagList; + private final GroovydocHolder holder; + + public Groovydoc(String content, GroovydocHolder groovydocHolder) { + this.content = content; + this.holder = groovydocHolder; + } + + public Groovydoc(final String content, final AnnotatedElement annotatedElement) { + this.content = content; + this.holder = new GroovydocHolder<AnnotatedElement>() { + @Override + public Groovydoc getGroovydoc() { + return Groovydoc.this; + } + + @Override + public AnnotatedElement getInstance() { + return annotatedElement; + } + }; + } + + /** + * Get the content of groovydoc + * @return the text content + */ + public String getContent() { + return content; + } + + /** + * TODO Get list of groovydoc tags + * @return a list of tags + */ + public List<GroovydocTag> getTagList() { + throw new UnsupportedOperationException("[TODO]parsing tags will be a new features of the next releases"); +// return tagList; + } + + /** + * Get the holder of the groovydoc + * @return the groovydoc holder + */ + public GroovydocHolder getHolder() { + return holder; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Groovydoc groovydoc = (Groovydoc) o; + return Objects.equals(content, groovydoc.content) && + Objects.equals(holder, groovydoc.holder); + } + + @Override + public int hashCode() { + return Objects.hash(content, holder); + } + + @Override + public String toString() { + return this.content; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/groovydoc/GroovydocHolder.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/groovydoc/GroovydocHolder.java b/src/main/groovy/groovy/lang/groovydoc/GroovydocHolder.java new file mode 100644 index 0000000..eb41df4 --- /dev/null +++ b/src/main/groovy/groovy/lang/groovydoc/GroovydocHolder.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang.groovydoc; + +/** + * Represents Groovydoc Holder + */ +public interface GroovydocHolder<T> { + String DOC_COMMENT = "_DOC_COMMENT"; // keys for meta data + /** + * Get the groovydoc + * @return the groovydoc + */ + Groovydoc getGroovydoc(); + + /** + * Get GroovydocHolder instance + * @return GroovydocHolder instance + */ + T getInstance(); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/groovydoc/GroovydocTag.java ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/groovydoc/GroovydocTag.java b/src/main/groovy/groovy/lang/groovydoc/GroovydocTag.java new file mode 100644 index 0000000..14e5aaa --- /dev/null +++ b/src/main/groovy/groovy/lang/groovydoc/GroovydocTag.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package groovy.lang.groovydoc; + +import java.util.Objects; + +/** + * TODO parse groovydoc to get tag content + */ +public class GroovydocTag { + private String name; + private String content; + private Groovydoc groovydoc; + + public GroovydocTag(String name, String content, Groovydoc groovydoc) { + this.name = name; + this.content = content; + this.groovydoc = groovydoc; + } + + public String getName() { + return name; + } + + public String getContent() { + return content; + } + + public Groovydoc getGroovydoc() { + return groovydoc; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GroovydocTag that = (GroovydocTag) o; + return Objects.equals(name, that.name) && + Objects.equals(content, that.content) && + Objects.equals(groovydoc, that.groovydoc); + } + + @Override + public int hashCode() { + return Objects.hash(name, content, groovydoc); + } + + @Override + public String toString() { + return content; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/lang/package.html ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/lang/package.html b/src/main/groovy/groovy/lang/package.html new file mode 100644 index 0000000..1ea84fc --- /dev/null +++ b/src/main/groovy/groovy/lang/package.html @@ -0,0 +1,28 @@ +<!-- + + 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. + +--> +<html> + <head> + <title>package groovy.lang.*</title> + </head> + <body> + <p>Core Groovy language classes for implementing data structures, closures, metadata and so forth.</p> + </body> +</html> http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy b/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy new file mode 100644 index 0000000..dcfe314 --- /dev/null +++ b/src/main/groovy/groovy/org/codehaus/groovy/transform/ASTTestTransformation.groovy @@ -0,0 +1,233 @@ +/* + * 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.transform + +import groovy.transform.CompilationUnitAware +import org.codehaus.groovy.ast.ASTNode +import org.codehaus.groovy.ast.AnnotationNode +import org.codehaus.groovy.ast.ClassCodeVisitorSupport +import org.codehaus.groovy.ast.ClassHelper +import org.codehaus.groovy.ast.ClassNode +import org.codehaus.groovy.ast.MethodNode +import org.codehaus.groovy.ast.expr.ClosureExpression +import org.codehaus.groovy.ast.expr.PropertyExpression +import org.codehaus.groovy.ast.expr.VariableExpression +import org.codehaus.groovy.ast.stmt.Statement +import org.codehaus.groovy.control.CompilationUnit +import org.codehaus.groovy.control.CompilePhase +import org.codehaus.groovy.control.CompilerConfiguration +import org.codehaus.groovy.control.ErrorCollector +import org.codehaus.groovy.control.Janitor +import org.codehaus.groovy.control.ProcessingUnit +import org.codehaus.groovy.control.SourceUnit +import org.codehaus.groovy.control.customizers.ImportCustomizer +import org.codehaus.groovy.control.io.ReaderSource +import org.codehaus.groovy.runtime.MethodClosure +import org.codehaus.groovy.syntax.SyntaxException +import org.codehaus.groovy.tools.Utilities + +import static org.codehaus.groovy.ast.tools.GeneralUtils.classX +import static org.codehaus.groovy.ast.tools.GeneralUtils.propX + +@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) +class ASTTestTransformation extends AbstractASTTransformation implements CompilationUnitAware { + private CompilationUnit compilationUnit + + void visit(final ASTNode[] nodes, final SourceUnit source) { + AnnotationNode annotationNode = nodes[0] + def member = annotationNode.getMember('phase') + def phase = null + if (member) { + if (member instanceof VariableExpression) { + phase = CompilePhase.valueOf(member.text) + } else if (member instanceof PropertyExpression) { + phase = CompilePhase.valueOf(member.propertyAsString) + } + annotationNode.setMember('phase', propX(classX(ClassHelper.make(CompilePhase)), phase.toString())) + } + member = annotationNode.getMember('value') + if (member && !(member instanceof ClosureExpression)) { + throw new SyntaxException("ASTTest value must be a closure", member.getLineNumber(), member.getColumnNumber()) + } + if (!member && !annotationNode.getNodeMetaData(ASTTestTransformation)) { + throw new SyntaxException("Missing test expression", annotationNode.getLineNumber(), annotationNode.getColumnNumber()) + } + // convert value into node metadata so that the expression doesn't mix up with other AST xforms like type checking + annotationNode.putNodeMetaData(ASTTestTransformation, member) + annotationNode.getMembers().remove('value') + + def pcallback = compilationUnit.progressCallback + def callback = new CompilationUnit.ProgressCallback() { + Binding binding = new Binding([:].withDefault {null}) + + @Override + void call(final ProcessingUnit context, final int phaseRef) { + if (phase==null || phaseRef == phase.phaseNumber) { + ClosureExpression testClosure = nodes[0].getNodeMetaData(ASTTestTransformation) + StringBuilder sb = new StringBuilder() + for (int i = testClosure.lineNumber; i <= testClosure.lastLineNumber; i++) { + sb.append(source.source.getLine(i, new Janitor())).append('\n') + } + def testSource = sb.substring(testClosure.columnNumber + 1, sb.length()) + testSource = testSource.substring(0, testSource.lastIndexOf('}')) + CompilerConfiguration config = new CompilerConfiguration() + def customizer = new ImportCustomizer() + config.addCompilationCustomizers(customizer) + binding['sourceUnit'] = source + binding['node'] = nodes[1] + binding['lookup'] = new MethodClosure(LabelFinder, "lookup").curry(nodes[1]) + binding['compilationUnit'] = compilationUnit + binding['compilePhase'] = CompilePhase.fromPhaseNumber(phaseRef) + + GroovyShell shell = new GroovyShell(binding, config) + + source.AST.imports.each { + customizer.addImport(it.alias, it.type.name) + } + source.AST.starImports.each { + customizer.addStarImports(it.packageName) + } + source.AST.staticImports.each { + customizer.addStaticImport(it.value.alias, it.value.type.name, it.value.fieldName) + } + source.AST.staticStarImports.each { + customizer.addStaticStars(it.value.className) + } + shell.evaluate(testSource) + } + } + } + + if (pcallback!=null) { + if (pcallback instanceof ProgressCallbackChain) { + pcallback.addCallback(callback) + } else { + pcallback = new ProgressCallbackChain(pcallback, callback) + } + callback = pcallback + } + + compilationUnit.setProgressCallback(callback) + + } + + void setCompilationUnit(final CompilationUnit unit) { + this.compilationUnit = unit + } + + private static class AssertionSourceDelegatingSourceUnit extends SourceUnit { + private final ReaderSource delegate + + AssertionSourceDelegatingSourceUnit(final String name, final ReaderSource source, final CompilerConfiguration flags, final GroovyClassLoader loader, final ErrorCollector er) { + super(name, '', flags, loader, er) + delegate = source + } + + @Override + String getSample(final int line, final int column, final Janitor janitor) { + String sample = null; + String text = delegate.getLine(line, janitor); + + if (text != null) { + if (column > 0) { + String marker = Utilities.repeatString(" ", column - 1) + "^"; + + if (column > 40) { + int start = column - 30 - 1; + int end = (column + 10 > text.length() ? text.length() : column + 10 - 1); + sample = " " + text.substring(start, end) + Utilities.eol() + " " + + marker.substring(start, marker.length()); + } else { + sample = " " + text + Utilities.eol() + " " + marker; + } + } else { + sample = text; + } + } + + return sample; + + } + + } + + private static class ProgressCallbackChain extends CompilationUnit.ProgressCallback { + + private final List<CompilationUnit.ProgressCallback> chain = new LinkedList<CompilationUnit.ProgressCallback>() + + ProgressCallbackChain(CompilationUnit.ProgressCallback... callbacks) { + if (callbacks!=null) { + callbacks.each { addCallback(it) } + } + } + + public void addCallback(CompilationUnit.ProgressCallback callback) { + chain << callback + } + + @Override + void call(final ProcessingUnit context, final int phase) { + chain*.call(context, phase) + } + } + + public static class LabelFinder extends ClassCodeVisitorSupport { + + public static List<Statement> lookup(MethodNode node, String label) { + LabelFinder finder = new LabelFinder(label, null) + node.code.visit(finder) + + finder.targets + } + + public static List<Statement> lookup(ClassNode node, String label) { + LabelFinder finder = new LabelFinder(label, null) + node.methods*.code*.visit(finder) + node.declaredConstructors*.code*.visit(finder) + + finder.targets + } + + private final String label + private final SourceUnit unit + + private final List<Statement> targets = new LinkedList<Statement>(); + + LabelFinder(final String label, final SourceUnit unit) { + this.label = label + this.unit = unit; + } + + @Override + protected SourceUnit getSourceUnit() { + unit + } + + @Override + protected void visitStatement(final Statement statement) { + super.visitStatement(statement) + if (statement.statementLabel==label) targets << statement + } + + List<Statement> getTargets() { + return Collections.unmodifiableList(targets) + } + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy b/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy new file mode 100644 index 0000000..2cda121 --- /dev/null +++ b/src/main/groovy/groovy/org/codehaus/groovy/transform/ConditionalInterruptibleASTTransformation.groovy @@ -0,0 +1,145 @@ +/* + * 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.transform + +import groovy.transform.ConditionalInterrupt +import org.codehaus.groovy.ast.AnnotatedNode +import org.codehaus.groovy.ast.AnnotationNode +import org.codehaus.groovy.ast.ClassHelper +import org.codehaus.groovy.ast.ClassNode +import org.codehaus.groovy.ast.FieldNode +import org.codehaus.groovy.ast.MethodNode +import org.codehaus.groovy.ast.Parameter +import org.codehaus.groovy.ast.PropertyNode +import org.codehaus.groovy.ast.expr.ArgumentListExpression +import org.codehaus.groovy.ast.expr.ClosureExpression +import org.codehaus.groovy.ast.expr.Expression +import org.codehaus.groovy.ast.expr.MethodCallExpression +import org.codehaus.groovy.ast.expr.VariableExpression +import org.codehaus.groovy.ast.tools.ClosureUtils +import org.codehaus.groovy.control.CompilePhase + +/** + * Allows "interrupt-safe" executions of scripts by adding a custom conditional + * check on loops (for, while, do) and first statement of closures. By default, also adds an interrupt check + * statement on the beginning of method calls. + * + * @see groovy.transform.ConditionalInterrupt + * @author Cedric Champeau + * @author Hamlet D'Arcy + * @author Paul King + * @since 1.8.0 + */ +@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) +public class ConditionalInterruptibleASTTransformation extends AbstractInterruptibleASTTransformation { + + private static final ClassNode MY_TYPE = ClassHelper.make(ConditionalInterrupt) + + private ClosureExpression conditionNode + private String conditionMethod + private MethodCallExpression conditionCallExpression + private ClassNode currentClass + + protected ClassNode type() { + return MY_TYPE + } + + protected void setupTransform(AnnotationNode node) { + super.setupTransform(node) + def member = node.getMember("value") + if (!member || !(member instanceof ClosureExpression)) internalError("Expected closure value for annotation parameter 'value'. Found $member") + conditionNode = member; + conditionMethod = 'conditionalTransform' + node.hashCode() + '$condition' + conditionCallExpression = new MethodCallExpression(new VariableExpression('this'), conditionMethod, new ArgumentListExpression()) + } + + protected String getErrorMessage() { + 'Execution interrupted. The following condition failed: ' + convertClosureToSource(conditionNode) + } + + void visitClass(ClassNode type) { + currentClass = type + def method = type.addMethod(conditionMethod, ACC_PRIVATE | ACC_SYNTHETIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, conditionNode.code) + method.synthetic = true + if (applyToAllMembers) { + super.visitClass(type) + } + } + + protected Expression createCondition() { + conditionCallExpression + } + + @Override + void visitAnnotations(AnnotatedNode node) { + // this transformation does not apply on annotation nodes + // visiting could lead to stack overflows + } + + @Override + void visitField(FieldNode node) { + if (!node.isStatic() && !node.isSynthetic()) { + super.visitField node + } + } + + @Override + void visitProperty(PropertyNode node) { + if (!node.isStatic() && !node.isSynthetic()) { + super.visitProperty node + } + } + + @Override + void visitClosureExpression(ClosureExpression closureExpr) { + if (closureExpr == conditionNode) return // do not visit the closure from the annotation itself + def code = closureExpr.code + closureExpr.code = wrapBlock(code) + super.visitClosureExpression closureExpr + } + + @Override + void visitMethod(MethodNode node) { + if (node.name == conditionMethod && !node.isSynthetic()) return // do not visit the generated method + if (node.name == 'run' && currentClass.isScript() && node.parameters.length == 0) { + // the run() method should not have the statement added, otherwise the script binding won't be set before + // the condition is actually tested + super.visitMethod(node) + } else { + if (checkOnMethodStart && !node.isSynthetic() && !node.isStatic() && !node.isAbstract()) { + def code = node.code + node.code = wrapBlock(code); + } + if (!node.isSynthetic() && !node.isStatic()) super.visitMethod(node) + } + } + + /** + * Converts a ClosureExpression into the String source. + * @param expression a closure + * @return the source the closure was created from + */ + private String convertClosureToSource(ClosureExpression expression) { + try { + return ClosureUtils.convertClosureToSource(this.source.source, expression); + } catch(Exception e) { + return e.message + } + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/10110145/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy ---------------------------------------------------------------------- diff --git a/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy b/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy new file mode 100644 index 0000000..a4fb4c3 --- /dev/null +++ b/src/main/groovy/groovy/org/codehaus/groovy/transform/ThreadInterruptibleASTTransformation.groovy @@ -0,0 +1,98 @@ +/* + * 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.transform + +import groovy.transform.CompileStatic +import groovy.transform.ThreadInterrupt +import org.codehaus.groovy.ast.ClassHelper +import org.codehaus.groovy.ast.ClassNode +import org.codehaus.groovy.ast.MethodNode +import org.codehaus.groovy.ast.Parameter +import org.codehaus.groovy.ast.expr.ArgumentListExpression +import org.codehaus.groovy.ast.expr.ClassExpression +import org.codehaus.groovy.ast.expr.ClosureExpression +import org.codehaus.groovy.ast.expr.Expression +import org.codehaus.groovy.ast.expr.MethodCallExpression +import org.codehaus.groovy.control.CompilePhase + +/** + * Allows "interrupt-safe" executions of scripts by adding Thread.currentThread().isInterrupted() + * checks on loops (for, while, do) and first statement of closures. By default, also adds an interrupt check + * statement on the beginning of method calls. + * + * @see groovy.transform.ThreadInterrupt + * + * @author Cedric Champeau + * @author Hamlet D'Arcy + * + * @since 1.8.0 + */ +@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION) +@CompileStatic +public class ThreadInterruptibleASTTransformation extends AbstractInterruptibleASTTransformation { + + private static final ClassNode MY_TYPE = ClassHelper.make(ThreadInterrupt) + private static final ClassNode THREAD_TYPE = ClassHelper.make(Thread) + private static final MethodNode CURRENTTHREAD_METHOD + private static final MethodNode ISINTERRUPTED_METHOD + + static { + CURRENTTHREAD_METHOD = THREAD_TYPE.getMethod('currentThread', Parameter.EMPTY_ARRAY) + ISINTERRUPTED_METHOD = THREAD_TYPE.getMethod('isInterrupted', Parameter.EMPTY_ARRAY) + } + + protected ClassNode type() { + return MY_TYPE; + } + + protected String getErrorMessage() { + 'Execution interrupted. The current thread has been interrupted.' + } + + protected Expression createCondition() { + def currentThread = new MethodCallExpression(new ClassExpression(THREAD_TYPE), + 'currentThread', + ArgumentListExpression.EMPTY_ARGUMENTS) + currentThread.methodTarget = CURRENTTHREAD_METHOD + def isInterrupted = new MethodCallExpression( + currentThread, + 'isInterrupted', ArgumentListExpression.EMPTY_ARGUMENTS) + isInterrupted.methodTarget = ISINTERRUPTED_METHOD + [currentThread, isInterrupted]*.implicitThis = false + + isInterrupted + } + + + @Override + public void visitClosureExpression(ClosureExpression closureExpr) { + def code = closureExpr.code + closureExpr.code = wrapBlock(code) + super.visitClosureExpression closureExpr + } + + @Override + public void visitMethod(MethodNode node) { + if (checkOnMethodStart && !node.isSynthetic() && !node.isAbstract()) { + def code = node.code + node.code = wrapBlock(code); + } + super.visitMethod(node) + } +}
