You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/12/20 04:29:36 UTC
[28/47] groovy git commit: Move source files to proper packages
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/builder/SimpleStrategy.java b/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
new file mode 100644
index 0000000..7956ac6
--- /dev/null
+++ b/src/main/groovy/groovy/transform/builder/SimpleStrategy.java
@@ -0,0 +1,132 @@
+/*
+ * 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.transform.builder;
+
+import groovy.transform.Undefined;
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.transform.AbstractASTTransformation;
+import org.codehaus.groovy.transform.BuilderASTTransformation;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.codehaus.groovy.ast.tools.GeneralUtils.assignX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.block;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.callThisX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.fieldX;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstancePropertyFields;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.param;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.params;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt;
+import static org.codehaus.groovy.ast.tools.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.newClass;
+import static org.codehaus.groovy.transform.AbstractASTTransformation.getMemberStringValue;
+import static org.codehaus.groovy.transform.BuilderASTTransformation.NO_EXCEPTIONS;
+
+/**
+ * This strategy is used with the {@link Builder} AST transform to modify your Groovy objects so that the
+ * setter methods for properties return the original object, thus allowing chained usage of the setters.
+ *
+ * You use it as follows:
+ * <pre class="groovyTestCase">
+ * import groovy.transform.builder.*
+ *
+ * {@code @Builder}(builderStrategy=SimpleStrategy)
+ * class Person {
+ * String firstName
+ * String lastName
+ * int age
+ * }
+ * def person = new Person().setFirstName("Robert").setLastName("Lewandowski").setAge(21)
+ * assert person.firstName == "Robert"
+ * assert person.lastName == "Lewandowski"
+ * assert person.age == 21
+ * </pre>
+ * The {@code prefix} annotation attribute can be used to create setters with a different naming convention, e.g. with the prefix set to the empty String, you would use your setters as follows:
+ * <pre>
+ * def p1 = new Person().firstName("Robert").lastName("Lewandowski").age(21)
+ * </pre>
+ * or using a prefix of 'with':
+ * <pre>
+ * def p2 = new Person().withFirstName("Robert").withLastName("Lewandowski").withAge(21)
+ * </pre>
+ * When using the default prefix of "set", Groovy's normal setters will be replaced by the chained versions. When using
+ * a custom prefix, Groovy's unchained setters will still be available for use in the normal unchained fashion.
+ *
+ * The 'useSetters' annotation attribute can be used for writable properties as per the {@code Builder} transform documentation.
+ * The other annotation attributes for the {@code @Builder} transform for configuring the building process aren't applicable for this strategy.
+ *
+ * @author Paul King
+ */
+public class SimpleStrategy extends BuilderASTTransformation.AbstractBuilderStrategy {
+ public void build(BuilderASTTransformation transform, AnnotatedNode annotatedNode, AnnotationNode anno) {
+ if (!(annotatedNode instanceof ClassNode)) {
+ transform.addError("Error during " + BuilderASTTransformation.MY_TYPE_NAME + " processing: building for " +
+ annotatedNode.getClass().getSimpleName() + " not supported by " + getClass().getSimpleName(), annotatedNode);
+ return;
+ }
+ ClassNode buildee = (ClassNode) annotatedNode;
+ if (unsupportedAttribute(transform, anno, "builderClassName")) return;
+ if (unsupportedAttribute(transform, anno, "buildMethodName")) return;
+ if (unsupportedAttribute(transform, anno, "builderMethodName")) return;
+ if (unsupportedAttribute(transform, anno, "forClass")) return;
+ if (unsupportedAttribute(transform, anno, "includeSuperProperties")) return;
+ if (unsupportedAttribute(transform, anno, "allProperties")) return;
+ boolean useSetters = transform.memberHasValue(anno, "useSetters", true);
+ boolean allNames = transform.memberHasValue(anno, "allNames", true);
+
+ List<String> excludes = new ArrayList<String>();
+ List<String> includes = new ArrayList<String>();
+ includes.add(Undefined.STRING);
+ if (!getIncludeExclude(transform, anno, buildee, excludes, includes)) return;
+ if (includes.size() == 1 && Undefined.isUndefined(includes.get(0))) includes = null;
+ String prefix = getMemberStringValue(anno, "prefix", "set");
+ List<FieldNode> fields = getFields(transform, anno, buildee);
+ if (includes != null) {
+ for (String name : includes) {
+ checkKnownField(transform, anno, name, fields);
+ }
+ }
+ for (FieldNode field : fields) {
+ String fieldName = field.getName();
+ if (!AbstractASTTransformation.shouldSkipUndefinedAware(fieldName, excludes, includes, allNames)) {
+ String methodName = getSetterName(prefix, fieldName);
+ Parameter parameter = param(field.getType(), fieldName);
+ buildee.addMethod(methodName, Opcodes.ACC_PUBLIC, newClass(buildee), params(parameter), NO_EXCEPTIONS, block(
+ stmt(useSetters && !field.isFinal()
+ ? callThisX(getSetterName("set", fieldName), varX(parameter))
+ : assignX(fieldX(field), varX(parameter))
+ ),
+ returnS(varX("this")))
+ );
+ }
+ }
+ }
+
+ @Override
+ protected List<FieldNode> getFields(BuilderASTTransformation transform, AnnotationNode anno, ClassNode buildee) {
+ return getInstancePropertyFields(buildee);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/ClosureParams.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/ClosureParams.java b/src/main/groovy/groovy/transform/stc/ClosureParams.java
new file mode 100644
index 0000000..e788d44
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ClosureParams.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 groovy.transform.stc;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Parameter annotation aimed at helping IDEs or the static type checker to infer the
+ * parameter types of a closure. Without this annotation, a method signature may look like
+ * this:<p>
+ * <code>public <T,R> List<R> doSomething(List<T> source, Closure<R> consumer)</code>
+ * <p>
+ * <p>The problem this annotation tries to solve is to define the expected parameter types of the
+ * <i>consumer</i> closure. The generics type defined in <code>Closure<R></code> correspond to the
+ * result type of the closure, but tell nothing about what the closure must accept as arguments.</p>
+ * <p></p>
+ * <p>There's no way in Java or Groovy to express the type signature of the expected closure call method from
+ * outside the closure itself, so we rely on an annotation here. Unfortunately, annotations also have limitations
+ * (like not being able to use generics placeholder as annotation values) that prevent us from expressing the
+ * type directly.</p>
+ * <p>Additionally, closures are polymorphic. This means that a single closure can be used with different, valid,
+ * parameter signatures. A typical use case can be found when a closure accepts either a {@link java.util.Map.Entry}
+ * or a (key,value) pair, like the {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#each(java.util.Map, groovy.lang.Closure)}
+ * method.</p>
+ * <p>For those reasons, the {@link ClosureParams} annotation takes these arguments:
+ * <ul>
+ * <li>{@link ClosureParams#value()} defines a {@link groovy.transform.stc.ClosureSignatureHint} hint class
+ * that the compiler will use to infer the parameter types</li>
+ * <li>{@link ClosureParams#conflictResolutionStrategy()} defines a {@link groovy.transform.stc.ClosureSignatureConflictResolver} resolver
+ * class that the compiler will use to potentially reduce ambiguities remaining after initial inference calculations</li>
+ * <li>{@link ClosureParams#options()}, a set of options that are passed to the hint when the type is inferred (and also available to the resolver)</li>
+ * </ul>
+ * </p>
+ * <p>As a result, the previous signature can be written like this:</p>
+ * <code>public <T,R> List<R> doSomething(List<T> source, @ClosureParams(FirstParam.FirstGenericType.class) Closure<R> consumer)</code>
+ * <p>Which uses the {@link FirstParam.FirstGenericType} first generic type of the first argument</p> hint to tell that the only expected
+ * argument type corresponds to the type of the first generic argument type of the first method parameter.
+ */
+@Target(ElementType.PARAMETER)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ClosureParams {
+ Class<? extends ClosureSignatureHint> value();
+ Class<? extends ClosureSignatureConflictResolver> conflictResolutionStrategy() default ClosureSignatureConflictResolver.class;
+ String[] options() default {};
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.java b/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.java
new file mode 100644
index 0000000..d727958
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ClosureSignatureConflictResolver.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.transform.stc;
+
+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.Expression;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.List;
+
+/**
+ * If multiple candidate signatures are found after applying type hints,
+ * a conflict resolver can attempt to resolve the ambiguity.
+ *
+ * @since 2.5.0
+ */
+public class ClosureSignatureConflictResolver {
+ /**
+ *
+ * @param candidates the list of signatures as determined after applying type hints and performing initial inference calculations
+ * @param receiver the receiver the method is being called on
+ * @param arguments the arguments for the closure
+ * @param closure the closure expression under analysis
+ * @param methodNode the method for which a {@link groovy.lang.Closure} parameter was annotated with {@link ClosureParams}
+ * @param sourceUnit the source unit of the file being compiled
+ * @param compilationUnit the compilation unit of the file being compiled
+ * @param options the options, corresponding to the {@link ClosureParams#options()} found on the annotation
+ * @return a non-null list of signatures, where a signature corresponds to an array of class nodes, each of them matching a parameter. A list with more than one element indicates that all ambiguities haven't yet been resolved.
+ */
+ public List<ClassNode[]> resolve(List<ClassNode[]> candidates, ClassNode receiver, Expression arguments, ClosureExpression closure,
+ MethodNode methodNode, SourceUnit sourceUnit, CompilationUnit compilationUnit, String[] options) {
+ // do nothing by default
+ return candidates;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java b/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java
new file mode 100644
index 0000000..9a77d20
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ClosureSignatureHint.java
@@ -0,0 +1,144 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.List;
+
+/**
+ * <p>A closure signature hint class is always used in conjunction with the {@link ClosureParams} annotation. It is
+ * called at compile time (or may be used by IDEs) to infer the types of the parameters of a {@link groovy.lang.Closure}.</p>
+
+ * <p>A closure hint class is responsible for generating the list of arguments that a closure accepts. Since closures
+ * may accept several signatures, {@link #getClosureSignatures(org.codehaus.groovy.ast.MethodNode, org.codehaus.groovy.control.SourceUnit, org.codehaus.groovy.control.CompilationUnit, String[], org.codehaus.groovy.ast.ASTNode)} should
+ * return a list.</p>
+ *
+ * <p>Whenever the type checker encounters a method call that targets a method accepting a closure, it will search
+ * for the {@link ClosureParams} annotation on the {@link groovy.lang.Closure} argument. If it is found, then it
+ * creates an instance of the hint class and calls the {@link #getClosureSignatures(org.codehaus.groovy.ast.MethodNode, org.codehaus.groovy.control.SourceUnit, org.codehaus.groovy.control.CompilationUnit, String[], org.codehaus.groovy.ast.ASTNode)}
+ * method, which will in turn return the list of signatures.</p>
+ *
+ * <p><i>Note that the signature concept here is used only to describe the parameter types, not the result type, which
+ * is found in the generic type argument of the {@link groovy.lang.Closure} class.</i></p>
+ *
+ * <p>Several predefined hints can be found, which should cover most of the use cases.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ *
+ */
+public abstract class ClosureSignatureHint {
+
+ /**
+ * A helper method which will extract the n-th generic type from a class node.
+ * @param type the class node from which to pick a generic type
+ * @param gtIndex the index of the generic type to extract
+ * @return the n-th generic type, or {@link org.codehaus.groovy.ast.ClassHelper#OBJECT_TYPE} if it doesn't exist.
+ */
+ public static ClassNode pickGenericType(ClassNode type, int gtIndex) {
+ final GenericsType[] genericsTypes = type.getGenericsTypes();
+ if (genericsTypes==null || genericsTypes.length<gtIndex) {
+ return ClassHelper.OBJECT_TYPE;
+ }
+ return genericsTypes[gtIndex].getType();
+ }
+
+ /**
+ * A helper method which will extract the n-th generic type from the n-th parameter of a method node.
+ * @param node the method node from which the generic type should be picked
+ * @param parameterIndex the index of the parameter in the method parameter list
+ * @param gtIndex the index of the generic type to extract
+ * @return the generic type, or {@link org.codehaus.groovy.ast.ClassHelper#OBJECT_TYPE} if it doesn't exist.
+ */
+ public static ClassNode pickGenericType(MethodNode node, int parameterIndex, int gtIndex) {
+ final Parameter[] parameters = node.getParameters();
+ final ClassNode type = parameters[parameterIndex].getOriginType();
+ return pickGenericType(type, gtIndex);
+ }
+
+ /**
+ * <p>Subclasses should implement this method, which returns the list of accepted closure signatures.</p>
+ *
+ * <p>The compiler will call this method each time, in a source file, a method call using a closure
+ * literal is encountered and that the target method has the corresponding {@link groovy.lang.Closure} parameter
+ * annotated with {@link groovy.transform.stc.ClosureParams}. So imagine the following code needs to be compiled:</p>
+ *
+ * <code>@groovy.transform.TypeChecked
+ * void doSomething() {
+ * println ['a','b'].collect { it.toUpperCase() }
+ * }</code>
+ *
+ * <p>The <i>collect</i> method accepts a closure, but normally, the type checker doesn't have enough type information
+ * in the sole {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#collect(java.util.Collection, groovy.lang.Closure)} method
+ * signature to infer the type of <i>it</i>. With the annotation, it will now try to find an annotation on the closure parameter.
+ * If it finds it, then an instance of the hint class is created and the type checker calls it with the following arguments:</p>
+ * <ul>
+ * <li>the method node corresponding to the target method (here, the {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#collect(java.util.Collection, groovy.lang.Closure)} method</li>
+ * <li>the (optional) list of options found in the annotation</li>
+ * </ul>
+ *
+ * <p>Now, the hint instance can return the list of expected parameters. Here, it would have to say that the collect method accepts
+ * a closure for which the only argument is of the type of the first generic type of the first argument.</p>
+ * <p>With that type information, the type checker can now infer that the type of <i>it</i> is <i>String</i>, because the first argument (here the receiver of the collect method)
+ * is a <i>List<String></i></p>
+ *
+ * <p></p>
+ *
+ * <p>Subclasses are therefore expected to return the signatures according to the available context, which is only the target method and the potential options.</p>
+ *
+ *
+ * @param node the method node for which a {@link groovy.lang.Closure} parameter was annotated with
+ * {@link ClosureParams}
+ * @param sourceUnit the source unit of the file being compiled
+ * @param compilationUnit the compilation unit of the file being compiled
+ * @param options the options, corresponding to the {@link ClosureParams#options()} found on the annotation @return a non-null list of signature, where a signature corresponds to an array of class nodes, each of them matching a parameter.
+ * @param usage the AST node, in the compiled file, which triggered a call to this method. Normally only used for logging/error handling
+ */
+ public abstract List<ClassNode[]> getClosureSignatures(MethodNode node, SourceUnit sourceUnit, CompilationUnit compilationUnit, String[] options, ASTNode usage);
+
+ /**
+ * Finds a class node given a string representing the type. Performs a lookup in the compilation unit to check if it is done in the same source unit.
+ * @param sourceUnit source unit
+ * @param compilationUnit compilation unit
+ * @param className the name of the class we want to get a {@link org.codehaus.groovy.ast.ClassNode} for
+ * @return a ClassNode representing the type
+ */
+ protected ClassNode findClassNode(final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String className) {
+ if (className.endsWith("[]")) {
+ return findClassNode(sourceUnit, compilationUnit, className.substring(0, className.length() - 2)).makeArray();
+ }
+ ClassNode cn = compilationUnit.getClassNode(className);
+ if (cn == null) {
+ try {
+ cn = ClassHelper.make(Class.forName(className, false, sourceUnit.getClassLoader()));
+ } catch (ClassNotFoundException e) {
+ cn = ClassHelper.make(className);
+ }
+ }
+ return cn;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/FirstParam.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/FirstParam.java b/src/main/groovy/groovy/transform/stc/FirstParam.java
new file mode 100644
index 0000000..81eb0e7
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/FirstParam.java
@@ -0,0 +1,93 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * <p>A hint used to instruct the type checker to pick the first parameter type. For example:</p>
+ * <code>public <T> def doWith(T src, @ClosureParams(FirstParam.class) Closure c) { c.call(src); }</code>
+ *
+ * <p>This class has several inner classes that also helps picking generic argument types instead of the parameter type.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class FirstParam extends PickAnyArgumentHint {
+ public FirstParam() {
+ super(0,-1);
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the first generic type of the first parameter type. For example:</p>
+ * <code>void <T> doWithElements(List<T> src, @ClosureParams(FirstParam.FirstGenericType.class) Closure c) { src.each { c.call(it) } }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class FirstGenericType extends PickAnyArgumentHint {
+ public FirstGenericType() {
+ super(0,0);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the second generic type of the first parameter type. For example:</p>
+ * <code>void <T,U> doWithElements(Tuple<T,U> src, @ClosureParams(FirstParam.SecondGenericType.class) Closure c) { src.each { c.call(it) } }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class SecondGenericType extends PickAnyArgumentHint {
+ public SecondGenericType() {
+ super(0,1);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the third generic type of the first parameter type. For example:</p>
+ * <code>void <T,U,V> doWithElements(Triple<T,U,V> src, @ClosureParams(FirstParam.ThirdGenericType.class) Closure c) { src.each { c.call(it) } }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class ThirdGenericType extends PickAnyArgumentHint {
+ public ThirdGenericType() {
+ super(0,2);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the type of the component of the first parameter type, which is therefore
+ * expected to be an array, like in this example:</p>
+ * <code>void <T> doWithArray(T[] array, @ClosureParams(FirstParam.Component.class) Closure c) { array.each { c.call(it)} }</code>
+ */
+ public static class Component extends FirstParam {
+ @Override
+ public ClassNode[] getParameterTypes(final MethodNode node, final String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode usage) {
+ final ClassNode[] parameterTypes = super.getParameterTypes(node, options, sourceUnit, unit, usage);
+ parameterTypes[0] = parameterTypes[0].getComponentType();
+ return parameterTypes;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.java b/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.java
new file mode 100644
index 0000000..e5125f1
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/FromAbstractTypeMethods.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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.transform.trait.Traits;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This signature hint uses abstract methods from some type (abstract class or interface) in order
+ * to infer the expected parameter types. This is especially useful for closure parameter type
+ * inference when implicit closure coercion is in action.
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class FromAbstractTypeMethods extends ClosureSignatureHint {
+ @Override
+ public List<ClassNode[]> getClosureSignatures(final MethodNode node, final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] options, final ASTNode usage) {
+ String className = options[0];
+ ClassNode cn = findClassNode(sourceUnit, compilationUnit, className);
+ return extractSignaturesFromMethods(cn);
+ }
+
+ private static List<ClassNode[]> extractSignaturesFromMethods(final ClassNode cn) {
+ List<MethodNode> methods = cn.getAllDeclaredMethods();
+ List<ClassNode[]> signatures = new LinkedList<ClassNode[]>();
+ for (MethodNode method : methods) {
+ if (!method.isSynthetic() && method.isAbstract()) {
+ extractParametersFromMethod(signatures, method);
+ }
+ }
+ return signatures;
+ }
+
+ private static void extractParametersFromMethod(final List<ClassNode[]> signatures, final MethodNode method) {
+ if (Traits.hasDefaultImplementation(method)) return;
+ Parameter[] parameters = method.getParameters();
+ ClassNode[] typeParams = new ClassNode[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ typeParams[i] = parameters[i].getOriginType();
+ }
+ signatures.add(typeParams);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/FromString.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/FromString.java b/src/main/groovy/groovy/transform/stc/FromString.java
new file mode 100644
index 0000000..12f9371
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/FromString.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package groovy.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.tools.GenericsUtils;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * <p>A closure parameter hint class that is convenient if you want to use a String representation
+ * of the signature. It makes use of the {@link ClosureParams#options() option strings}, where
+ * each string corresponds to a single signature.</p>
+ *
+ * <p>The following example describes a closure as accepting a single signature (List<T> list ->):</p>
+ *
+ * <code>public <T> T apply(T src, @ClosureParams(value=FromString.class, options="List<T>" Closure<T> cl)</code>
+ *
+ * <p>The next example describes a closure as accepting two signatures (List<T> list ->) and (T t ->):</p>
+ *
+ * <code>public <T> T apply(T src, @ClosureParams(value=FromString.class, options={"List<T>","T"} Closure<T> cl)</code>
+ *
+ * <p>It is advisable not to use this hint as a replacement for the various {@link FirstParam}, {@link SimpleType},
+ * ... hints because it is actually much slower. Using this hint should therefore be limited
+ * to cases where it's not possible to express the signature using the existing hints.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class FromString extends ClosureSignatureHint {
+
+ @Override
+ public List<ClassNode[]> getClosureSignatures(final MethodNode node, final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] options, final ASTNode usage) {
+ List<ClassNode[]> list = new ArrayList<ClassNode[]>(options.length);
+ for (String option : options) {
+ list.add(parseOption(option, sourceUnit, compilationUnit, node, usage));
+ }
+ return list;
+ }
+
+ /**
+ * Parses a string representing a type, that must be aligned with the current context.
+ * For example, <i>"List<T>"</i> must be converted into the appropriate ClassNode
+ * for which <i>T</i> matches the appropriate placeholder.
+ *
+ *
+ * @param option a string representing a type
+ * @param sourceUnit the source unit (of the file being compiled)
+ * @param compilationUnit the compilation unit (of the file being compiled)
+ * @param mn the method node
+ * @param usage
+ * @return a class node if it could be parsed and resolved, null otherwise
+ */
+ private static ClassNode[] parseOption(final String option, final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final MethodNode mn, final ASTNode usage) {
+ return GenericsUtils.parseClassNodesFromString(option, sourceUnit, compilationUnit, mn, usage);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java b/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java
new file mode 100644
index 0000000..aed167a
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/IncorrectTypeHintException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+public class IncorrectTypeHintException extends SyntaxException {
+ public IncorrectTypeHintException(final MethodNode mn, final Throwable e, int line, int column) {
+ super("Incorrect type hint in @ClosureParams in class "+mn.getDeclaringClass().getName()+" method "+mn.getTypeDescriptor()+" : "+e.getMessage(), e, line, column);
+ }
+
+ public IncorrectTypeHintException(final MethodNode mn, final String msg, final int line, final int column) {
+ super("Incorrect type hint in @ClosureParams in class "+mn.getDeclaringClass().getName()+" method "+mn.getTypeDescriptor()+" : "+msg, line, column);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java b/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java
new file mode 100644
index 0000000..9316ef8
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/MapEntryOrKeyValue.java
@@ -0,0 +1,119 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GenericsType;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>A special hint which handles a common use case in the Groovy methods that work on maps. In case of an
+ * iteration on a list of map entries, you often want the user to be able to work either on a {@link java.util.Map.Entry} map entry
+ * or on a key,value pair.</p>
+ * <p>The result is a closure which can have the following forms:</p>
+ * <ul>
+ * <li><code>{ key, value -> ...}</code> where key is the key of a map entry, and value the corresponding value</li>
+ * <li><code>{ entry -> ... }</code> where entry is a {@link java.util.Map.Entry} map entry</li>
+ * <li><code>{ ...}</code> where <i>it</i> is an implicit {@link java.util.Map.Entry} map entry</li>
+ * </ul>
+ * <p>This hint handles all those cases by picking the generics from the first argument of the method (by default).</p>
+ * <p>The options array is used to modify the behavior of this hint. Each string in the option array consists of
+ * a key=value pair.</p>
+ * <ul>
+ * <li><i>argNum=index</i> of the parameter representing the map (by default, 0)</li>
+ * <li><i>index=true or false</i>, by default false. If true, then an additional "int" parameter is added,
+ * for "withIndex" variants</li>
+ * </ul>
+ * <code>void doSomething(String str, Map<K,>V map, @ClosureParams(value=MapEntryOrKeyValue.class,options="argNum=1") Closure c) { ... }</code>
+ */
+public class MapEntryOrKeyValue extends ClosureSignatureHint {
+ private static final ClassNode MAPENTRY_TYPE = ClassHelper.make(Map.Entry.class);
+
+ public List<ClassNode[]> getClosureSignatures(final MethodNode node, final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] options, final ASTNode usage) {
+ Options opt;
+ try {
+ opt = Options.parse(node, usage, options);
+ } catch (IncorrectTypeHintException e) {
+ sourceUnit.addError(e);
+ return Collections.emptyList();
+ }
+ GenericsType[] genericsTypes = node.getParameters()[opt.parameterIndex].getOriginType().getGenericsTypes();
+ if (genericsTypes==null) {
+ // would happen if you have a raw Map type for example
+ genericsTypes = new GenericsType[] {
+ new GenericsType(ClassHelper.OBJECT_TYPE),
+ new GenericsType(ClassHelper.OBJECT_TYPE)
+ };
+ }
+ ClassNode[] firstSig;
+ ClassNode[] secondSig;
+ ClassNode mapEntry = MAPENTRY_TYPE.getPlainNodeReference();
+ mapEntry.setGenericsTypes(genericsTypes);
+ if (opt.generateIndex) {
+ firstSig = new ClassNode[] {genericsTypes[0].getType(), genericsTypes[1].getType(), ClassHelper.int_TYPE};
+ secondSig = new ClassNode[] {mapEntry, ClassHelper.int_TYPE};
+
+ } else {
+ firstSig = new ClassNode[] {genericsTypes[0].getType(), genericsTypes[1].getType()};
+ secondSig = new ClassNode[] {mapEntry};
+ }
+ return Arrays.asList(firstSig, secondSig);
+ }
+
+ private static final class Options {
+ final int parameterIndex;
+ final boolean generateIndex;
+
+ private Options(final int parameterIndex, final boolean generateIndex) {
+ this.parameterIndex = parameterIndex;
+ this.generateIndex = generateIndex;
+ }
+
+ static Options parse(MethodNode mn, ASTNode source, String[] options) throws IncorrectTypeHintException {
+ int pIndex = 0;
+ boolean generateIndex = false;
+ for (String option : options) {
+ String[] keyValue = option.split("=");
+ if (keyValue.length==2) {
+ String key = keyValue[0];
+ String value = keyValue[1];
+ if ("argNum".equals(key)) {
+ pIndex = Integer.parseInt(value);
+ } else if ("index".equals(key)) {
+ generateIndex = Boolean.valueOf(value);
+ } else {
+ throw new IncorrectTypeHintException(mn, "Unrecognized option: "+key, source.getLineNumber(), source.getColumnNumber());
+ }
+ } else {
+ throw new IncorrectTypeHintException(mn, "Incorrect option format. Should be argNum=<num> or index=<boolean> ", source.getLineNumber(), source.getColumnNumber());
+ }
+ }
+ return new Options(pIndex, generateIndex);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java b/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java
new file mode 100644
index 0000000..6a5463f
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/PickAnyArgumentHint.java
@@ -0,0 +1,75 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * <p>Base class for hints which use the type of a parameter of the annotated method as the signature.
+ * This can optionally use a generic type of the selected parameter as the hint. For example, imagine the following
+ * method:</p>
+ * <code>void foo(A firstArg, B secondArg, Closure c) {...}</code>
+ * <p>If the <i>c</i> closure should be <code>{ B it -> ...}</code>, then we can see that the parameter type
+ * should be picked from the second parameter of the foo method, which is what {@link groovy.transform.stc.PickAnyArgumentHint}
+ * lets you do.</p>
+ * <p>Alternatively, the method may look like this:</p>
+ * <code>void <T> foo(A<T> firstArg, B secondArg, Closure c) {...}</code>
+ * <p>in which case if you want to express the fact that <i>c</i> should accept a <T> then you can use the
+ * {@link #genericTypeIndex} value.</p>
+ * <p></p>
+ * <p>This class is extended by several hint providers that make it easier to use as annotation values.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class PickAnyArgumentHint extends SingleSignatureClosureHint {
+ private final int parameterIndex;
+ private final int genericTypeIndex;
+
+ /**
+ * Creates the an argument picker which extracts the type of the first parameter.
+ */
+ public PickAnyArgumentHint() {
+ this(0,-1);
+ }
+
+ /**
+ * Creates a picker which will extract the parameterIndex-th parameter type, or its
+ * genericTypeIndex-th generic type genericTypeIndex is >=0.
+ * @param parameterIndex the index of the parameter from which to extract the type
+ * @param genericTypeIndex if >=0, then returns the corresponding generic type instead of the parameter type.
+ */
+ public PickAnyArgumentHint(final int parameterIndex, final int genericTypeIndex) {
+ this.parameterIndex = parameterIndex;
+ this.genericTypeIndex = genericTypeIndex;
+ }
+
+ @Override
+ public ClassNode[] getParameterTypes(final MethodNode node, final String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode usage) {
+ ClassNode type = node.getParameters()[parameterIndex].getOriginType();
+ if (genericTypeIndex>=0) {
+ type = pickGenericType(type, genericTypeIndex);
+ }
+ return new ClassNode[]{type};
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/PickFirstResolver.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/PickFirstResolver.java b/src/main/groovy/groovy/transform/stc/PickFirstResolver.java
new file mode 100644
index 0000000..0bebb3e
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/PickFirstResolver.java
@@ -0,0 +1,45 @@
+/*
+ * 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.transform.stc;
+
+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.Expression;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Returns the first of several candidates found.
+ * This is useful if several types should be supported but only the first
+ * should be the default/inferred type. Other options in the list are
+ * obtained through explicitly typing the parameter(s).
+ *
+ * @since 2.5.0
+ */
+public class PickFirstResolver extends ClosureSignatureConflictResolver {
+ @Override
+ public List<ClassNode[]> resolve(List<ClassNode[]> candidates, ClassNode receiver, Expression arguments, ClosureExpression closure,
+ MethodNode methodNode, SourceUnit sourceUnit, CompilationUnit compilationUnit, String[] options) {
+ return Collections.singletonList(candidates.get(0));
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/SecondParam.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/SecondParam.java b/src/main/groovy/groovy/transform/stc/SecondParam.java
new file mode 100644
index 0000000..c077750
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/SecondParam.java
@@ -0,0 +1,93 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * <p>A hint used to instruct the type checker to pick the second parameter type. For example:</p>
+ * <code>public <T,U> def doWith(T first, U second, @ClosureParams(SecondParam.class) Closure c) { c.call(src); }</code>
+ *
+ * <p>This class has several inner classes that also helps picking generic argument types instead of the parameter type.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class SecondParam extends PickAnyArgumentHint {
+ public SecondParam() {
+ super(1,-1);
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the first generic type of the second parameter type. For example:</p>
+ * <code>void <T> doWithElements(String base, List<T> src, @ClosureParams(SecondParam.FirstGenericType.class) Closure c) { ... } }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class FirstGenericType extends PickAnyArgumentHint {
+ public FirstGenericType() {
+ super(1,0);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the second generic type of the second parameter type. For example:</p>
+ * <code>void <T,U> doWithElements(String base, Tuple<T,U> src, @ClosureParams(SecondParam.SecondGenericType.class) Closure c) { ... }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class SecondGenericType extends PickAnyArgumentHint {
+ public SecondGenericType() {
+ super(1,1);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the second generic type of the second parameter type. For example:</p>
+ * <code>void <T,U,V> doWithElements(String base, Triple<T,U,V> src, @ClosureParams(SecondParam.ThirdGenericType.class) Closure c) { ... }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class ThirdGenericType extends PickAnyArgumentHint {
+ public ThirdGenericType() {
+ super(1,2);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the type of the component of the second parameter type, which is therefore
+ * expected to be an array, like in this example:</p>
+ * <code>void <T> doWithArray(String first, T[] array, @ClosureParams(FirstParam.Component.class) Closure c) { ... }</code>
+ */
+ public static class Component extends SecondParam {
+ @Override
+ public ClassNode[] getParameterTypes(final MethodNode node, final String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode usage) {
+ final ClassNode[] parameterTypes = super.getParameterTypes(node, options, sourceUnit, unit, usage);
+ parameterTypes[0] = parameterTypes[0].getComponentType();
+ return parameterTypes;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/SimpleType.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/SimpleType.java b/src/main/groovy/groovy/transform/stc/SimpleType.java
new file mode 100644
index 0000000..8866e3b
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/SimpleType.java
@@ -0,0 +1,36 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+public class SimpleType extends SingleSignatureClosureHint {
+ @Override
+ public ClassNode[] getParameterTypes(final MethodNode node, final String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode usage) {
+ ClassNode[] result = new ClassNode[options.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = findClassNode(sourceUnit, unit, options[i]);
+ }
+ return result;
+ }
+ }
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.java b/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.java
new file mode 100644
index 0000000..3db66ff
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/SingleSignatureClosureHint.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 groovy.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A simplified version of a {@link groovy.transform.stc.ClosureSignatureHint} which is suitable
+ * for monomorphic closures, that is to say closures which only respond to a single signature.
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public abstract class SingleSignatureClosureHint extends ClosureSignatureHint {
+
+ public abstract ClassNode[] getParameterTypes(final MethodNode node, final String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode usage);
+
+ public List<ClassNode[]> getClosureSignatures(final MethodNode node, final SourceUnit sourceUnit, final CompilationUnit compilationUnit, final String[] options, final ASTNode usage) {
+ return Collections.singletonList(getParameterTypes(node, options, sourceUnit, compilationUnit, usage));
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/transform/stc/ThirdParam.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/transform/stc/ThirdParam.java b/src/main/groovy/groovy/transform/stc/ThirdParam.java
new file mode 100644
index 0000000..c493e36
--- /dev/null
+++ b/src/main/groovy/groovy/transform/stc/ThirdParam.java
@@ -0,0 +1,94 @@
+/*
+ * 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.transform.stc;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * <p>A hint used to instruct the type checker to pick the third parameter type. For example:</p>
+ * <code>public <T,U,V> def doWith(T first, U second, V third, @ClosureParams(ThirdParam.class) Closure c) { ... }</code>
+ *
+ * <p>This class has several inner classes that also helps picking generic argument types instead of the parameter type.</p>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+public class ThirdParam extends PickAnyArgumentHint {
+ public ThirdParam() {
+ super(2,-1);
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the first generic type of the third parameter type. For example:</p>
+ * <code>void <T> doWithElements(String first, Integer second, List<T> third, @ClosureParams(SecondParam.FirstGenericType.class) Closure c) { ... } }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class FirstGenericType extends PickAnyArgumentHint {
+ public FirstGenericType() {
+ super(2,0);
+ }
+ }
+
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the second generic type of the third parameter type. For example:</p>
+ * <code>void <T,U> doWithElements(String first, Integer second, Tuple<T,U> third, @ClosureParams(SecondParam.SecondGenericType.class) Closure c) { ... }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class SecondGenericType extends PickAnyArgumentHint {
+ public SecondGenericType() {
+ super(2,1);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the second generic type of the third parameter type. For example:</p>
+ * <code>void <T,U,V> doWithElements(String first, Integer second, Triple<T,U,V> src, @ClosureParams(SecondParam.ThirdGenericType.class) Closure c) { ... }</code>
+ *
+ * @author Cédric Champeau
+ * @since 2.3.0
+ */
+ public static class ThirdGenericType extends PickAnyArgumentHint {
+ public ThirdGenericType() {
+ super(2,2);
+ }
+ }
+
+ /**
+ * <p>A hint used to instruct the type checker to pick the type of the component of the third parameter type, which is therefore
+ * expected to be an array, like in this example:</p>
+ * <code>void <T> doWithArray(String first, int second, T[] third, @ClosureParams(FirstParam.Component.class) Closure c) { ... }</code>
+ */
+ public static class Component extends ThirdParam {
+ @Override
+ public ClassNode[] getParameterTypes(final MethodNode node, final String[] options, final SourceUnit sourceUnit, final CompilationUnit unit, final ASTNode usage) {
+ final ClassNode[] parameterTypes = super.getParameterTypes(node, options, sourceUnit, unit, usage);
+ parameterTypes[0] = parameterTypes[0].getComponentType();
+ return parameterTypes;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0ad8c07c/src/main/groovy/groovy/ui/GroovyMain.java
----------------------------------------------------------------------
diff --git a/src/main/groovy/groovy/ui/GroovyMain.java b/src/main/groovy/groovy/ui/GroovyMain.java
new file mode 100644
index 0000000..cedb8fb
--- /dev/null
+++ b/src/main/groovy/groovy/ui/GroovyMain.java
@@ -0,0 +1,597 @@
+/*
+ * 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.ui;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.GroovyShell;
+import groovy.lang.GroovySystem;
+import groovy.lang.MissingMethodException;
+import groovy.lang.Script;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.customizers.ImportCustomizer;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+import org.codehaus.groovy.runtime.ResourceGroovyMethods;
+import org.codehaus.groovy.runtime.StackTraceUtils;
+import org.codehaus.groovy.runtime.StringGroovyMethods;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+import static org.apache.commons.cli.Option.builder;
+
+/**
+ * A Command line to execute groovy.
+ */
+public class GroovyMain {
+
+ // arguments to the script
+ private List args;
+
+ // is this a file on disk
+ private boolean isScriptFile;
+
+ // filename or content of script
+ private String script;
+
+ // process args as input files
+ private boolean processFiles;
+
+ // edit input files in place
+ private boolean editFiles;
+
+ // automatically output the result of each script
+ private boolean autoOutput;
+
+ // automatically split each line using the splitpattern
+ private boolean autoSplit;
+
+ // The pattern used to split the current line
+ private String splitPattern = " ";
+
+ // process sockets
+ private boolean processSockets;
+
+ // port to listen on when processing sockets
+ private int port;
+
+ // backup input files with extension
+ private String backupExtension;
+
+ // do you want full stack traces in script exceptions?
+ private boolean debug = false;
+
+ // Compiler configuration, used to set the encodings of the scripts/classes
+ private CompilerConfiguration conf = new CompilerConfiguration(System.getProperties());
+
+ /**
+ * Main CLI interface.
+ *
+ * @param args all command line args.
+ */
+ public static void main(String args[]) {
+ processArgs(args, System.out);
+ }
+
+ // package-level visibility for testing purposes (just usage/errors at this stage)
+ // TODO: should we have an 'err' printstream too for ParseException?
+ static void processArgs(String[] args, final PrintStream out) {
+ Options options = buildOptions();
+
+ try {
+ CommandLine cmd = parseCommandLine(options, args);
+
+ if (cmd.hasOption('h')) {
+ printHelp(out, options);
+ } else if (cmd.hasOption('v')) {
+ String version = GroovySystem.getVersion();
+ out.println("Groovy Version: " + version + " JVM: " + System.getProperty("java.version") +
+ " Vendor: " + System.getProperty("java.vm.vendor") + " OS: " + System.getProperty("os.name"));
+ } else {
+ // If we fail, then exit with an error so scripting frameworks can catch it
+ // TODO: pass printstream(s) down through process
+ if (!process(cmd)) {
+ System.exit(1);
+ }
+ }
+ } catch (ParseException pe) {
+ out.println("error: " + pe.getMessage());
+ printHelp(out, options);
+ } catch (IOException ioe) {
+ out.println("error: " + ioe.getMessage());
+ }
+ }
+
+ private static void printHelp(PrintStream out, Options options) {
+ HelpFormatter formatter = new HelpFormatter();
+ PrintWriter pw = new PrintWriter(out);
+
+ formatter.printHelp(
+ pw,
+ 80,
+ "groovy [options] [filename] [args]",
+ "The Groovy command line processor.\nOptions:",
+ options,
+ 2,
+ 4,
+ null, // footer
+ false);
+
+ pw.flush();
+ }
+
+ /**
+ * Parse the command line.
+ *
+ * @param options the options parser.
+ * @param args the command line args.
+ * @return parsed command line.
+ * @throws ParseException if there was a problem.
+ */
+ private static CommandLine parseCommandLine(Options options, String[] args) throws ParseException {
+ CommandLineParser parser = new DefaultParser();
+ return parser.parse(options, args, true);
+ }
+
+ /**
+ * Build the options parser.
+ *
+ * @return an options parser.
+ */
+ private static Options buildOptions() {
+ return new Options()
+ .addOption(builder("classpath").hasArg().argName("path").desc("Specify where to find the class files - must be first argument").build())
+ .addOption(builder("cp").longOpt("classpath").hasArg().argName("path").desc("Aliases for '-classpath'").build())
+ .addOption(builder("D").longOpt("define").desc("Define a system property").numberOfArgs(2).valueSeparator().argName("name=value").build())
+ .addOption(
+ builder().longOpt("disableopt")
+ .desc("Disables one or all optimization elements; " +
+ "optlist can be a comma separated list with the elements: " +
+ "all (disables all optimizations), " +
+ "int (disable any int based optimizations)")
+ .hasArg().argName("optlist").build())
+ .addOption(builder("h").hasArg(false).desc("Usage information").longOpt("help").build())
+ .addOption(builder("d").hasArg(false).desc("Debug mode will print out full stack traces").longOpt("debug").build())
+ .addOption(builder("v").hasArg(false).desc("Display the Groovy and JVM versions").longOpt("version").build())
+ .addOption(builder("c").argName("charset").hasArg().desc("Specify the encoding of the files").longOpt("encoding").build())
+ .addOption(builder("e").argName("script").hasArg().desc("Specify a command line script").build())
+ .addOption(builder("i").argName("extension").optionalArg(true).desc("Modify files in place; create backup if extension is given (e.g. \'.bak\')").build())
+ .addOption(builder("n").hasArg(false).desc("Process files line by line using implicit 'line' variable").build())
+ .addOption(builder("p").hasArg(false).desc("Process files line by line and print result (see also -n)").build())
+ .addOption(builder("pa").hasArg(false).desc("Generate metadata for reflection on method parameter names (jdk8+ only)").longOpt("parameters").build())
+ .addOption(builder("l").argName("port").optionalArg(true).desc("Listen on a port and process inbound lines (default: 1960)").build())
+ .addOption(builder("a").argName("splitPattern").optionalArg(true).desc("Split lines using splitPattern (default '\\s') using implicit 'split' variable").longOpt("autosplit").build())
+ .addOption(builder().longOpt("indy").desc("Enables compilation using invokedynamic").build())
+ .addOption(builder().longOpt("configscript").hasArg().desc("A script for tweaking the configuration options").build())
+ .addOption(builder("b").longOpt("basescript").hasArg().argName("class").desc("Base class name for scripts (must derive from Script)").build());
+ }
+
+ /**
+ * Process the users request.
+ *
+ * @param line the parsed command line.
+ * @throws ParseException if invalid options are chosen
+ */
+ private static boolean process(CommandLine line) throws ParseException, IOException {
+ List args = line.getArgList();
+
+ if (line.hasOption('D')) {
+ Properties optionProperties = line.getOptionProperties("D");
+ Enumeration<String> propertyNames = (Enumeration<String>) optionProperties.propertyNames();
+ while (propertyNames.hasMoreElements()) {
+ String nextName = propertyNames.nextElement();
+ System.setProperty(nextName, optionProperties.getProperty(nextName));
+ }
+ }
+
+ GroovyMain main = new GroovyMain();
+
+ // add the ability to parse scripts with a specified encoding
+ main.conf.setSourceEncoding(line.getOptionValue('c',main.conf.getSourceEncoding()));
+
+ main.isScriptFile = !line.hasOption('e');
+ main.debug = line.hasOption('d');
+ main.conf.setDebug(main.debug);
+ main.conf.setParameters(line.hasOption("pa"));
+ main.processFiles = line.hasOption('p') || line.hasOption('n');
+ main.autoOutput = line.hasOption('p');
+ main.editFiles = line.hasOption('i');
+ if (main.editFiles) {
+ main.backupExtension = line.getOptionValue('i');
+ }
+ main.autoSplit = line.hasOption('a');
+ String sp = line.getOptionValue('a');
+ if (sp != null)
+ main.splitPattern = sp;
+
+ if (main.isScriptFile) {
+ if (args.isEmpty())
+ throw new ParseException("neither -e or filename provided");
+
+ main.script = (String) args.remove(0);
+ if (main.script.endsWith(".java"))
+ throw new ParseException("error: cannot compile file with .java extension: " + main.script);
+ } else {
+ main.script = line.getOptionValue('e');
+ }
+
+ main.processSockets = line.hasOption('l');
+ if (main.processSockets) {
+ String p = line.getOptionValue('l', "1960"); // default port to listen to
+ main.port = Integer.parseInt(p);
+ }
+
+ // we use "," as default, because then split will create
+ // an empty array if no option is set
+ String disabled = line.getOptionValue("disableopt", ",");
+ String[] deopts = disabled.split(",");
+ for (String deopt_i : deopts) {
+ main.conf.getOptimizationOptions().put(deopt_i,false);
+ }
+
+ if (line.hasOption("indy")) {
+ CompilerConfiguration.DEFAULT.getOptimizationOptions().put("indy", true);
+ main.conf.getOptimizationOptions().put("indy", true);
+ }
+
+ if (line.hasOption("basescript")) {
+ main.conf.setScriptBaseClass(line.getOptionValue("basescript"));
+ }
+
+ String configScripts = System.getProperty("groovy.starter.configscripts", null);
+ if (line.hasOption("configscript") || (configScripts != null && !configScripts.isEmpty())) {
+ List<String> scripts = new ArrayList<String>();
+ if (line.hasOption("configscript")) {
+ scripts.add(line.getOptionValue("configscript"));
+ }
+ if (configScripts != null) {
+ scripts.addAll(StringGroovyMethods.tokenize((CharSequence) configScripts, ','));
+ }
+ processConfigScripts(scripts, main.conf);
+ }
+
+ main.args = args;
+ return main.run();
+ }
+
+ public static void processConfigScripts(List<String> scripts, CompilerConfiguration conf) throws IOException {
+ if (scripts.isEmpty()) return;
+ Binding binding = new Binding();
+ binding.setVariable("configuration", conf);
+ CompilerConfiguration configuratorConfig = new CompilerConfiguration();
+ ImportCustomizer customizer = new ImportCustomizer();
+ customizer.addStaticStars("org.codehaus.groovy.control.customizers.builder.CompilerCustomizationBuilder");
+ configuratorConfig.addCompilationCustomizers(customizer);
+ GroovyShell shell = new GroovyShell(binding, configuratorConfig);
+ for (String script : scripts) {
+ shell.evaluate(new File(script));
+ }
+ }
+
+
+ /**
+ * Run the script.
+ */
+ private boolean run() {
+ try {
+ if (processSockets) {
+ processSockets();
+ } else if (processFiles) {
+ processFiles();
+ } else {
+ processOnce();
+ }
+ return true;
+ } catch (CompilationFailedException e) {
+ System.err.println(e);
+ return false;
+ } catch (Throwable e) {
+ if (e instanceof InvokerInvocationException) {
+ InvokerInvocationException iie = (InvokerInvocationException) e;
+ e = iie.getCause();
+ }
+ System.err.println("Caught: " + e);
+ if (!debug) {
+ StackTraceUtils.deepSanitize(e);
+ }
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * Process Sockets.
+ */
+ private void processSockets() throws CompilationFailedException, IOException, URISyntaxException {
+ GroovyShell groovy = new GroovyShell(conf);
+ new GroovySocketServer(groovy, getScriptSource(isScriptFile, script), autoOutput, port);
+ }
+
+ /**
+ * Get the text of the Groovy script at the given location.
+ * If the location is a file path and it does not exist as given,
+ * then {@link GroovyMain#huntForTheScriptFile(String)} is called to try
+ * with some Groovy extensions appended.
+ *
+ * This method is not used to process scripts and is retained for backward
+ * compatibility. If you want to modify how GroovyMain processes scripts
+ * then use {@link GroovyMain#getScriptSource(boolean, String)}.
+ *
+ * @param uriOrFilename
+ * @return the text content at the location
+ * @throws IOException
+ * @deprecated
+ */
+ @Deprecated
+ public String getText(String uriOrFilename) throws IOException {
+ if (URI_PATTERN.matcher(uriOrFilename).matches()) {
+ try {
+ return ResourceGroovyMethods.getText(new URL(uriOrFilename));
+ } catch (Exception e) {
+ throw new GroovyRuntimeException("Unable to get script from URL: ", e);
+ }
+ }
+ return ResourceGroovyMethods.getText(huntForTheScriptFile(uriOrFilename));
+ }
+
+ /**
+ * Get a new GroovyCodeSource for a script which may be given as a location
+ * (isScript is true) or as text (isScript is false).
+ *
+ * @param isScriptFile indicates whether the script parameter is a location or content
+ * @param script the location or context of the script
+ * @return a new GroovyCodeSource for the given script
+ * @throws IOException
+ * @throws URISyntaxException
+ * @since 2.3.0
+ */
+ protected GroovyCodeSource getScriptSource(boolean isScriptFile, String script) throws IOException, URISyntaxException {
+ //check the script is currently valid before starting a server against the script
+ if (isScriptFile) {
+ // search for the file and if it exists don't try to use URIs ...
+ File scriptFile = huntForTheScriptFile(script);
+ if (!scriptFile.exists() && URI_PATTERN.matcher(script).matches()) {
+ return new GroovyCodeSource(new URI(script));
+ }
+ return new GroovyCodeSource( scriptFile );
+ }
+ return new GroovyCodeSource(script, "script_from_command_line", GroovyShell.DEFAULT_CODE_BASE);
+ }
+
+ // RFC2396
+ // scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ // match URIs but not Windows filenames, e.g.: http://cnn.com but not C:\xxx\file.ext
+ private static final Pattern URI_PATTERN = Pattern.compile("\\p{Alpha}[-+.\\p{Alnum}]*:[^\\\\]*");
+
+ /**
+ * Search for the script file, doesn't bother if it is named precisely.
+ *
+ * Tries in this order:
+ * - actual supplied name
+ * - name.groovy
+ * - name.gvy
+ * - name.gy
+ * - name.gsh
+ *
+ * @since 2.3.0
+ */
+ public static File searchForGroovyScriptFile(String input) {
+ String scriptFileName = input.trim();
+ File scriptFile = new File(scriptFileName);
+ // TODO: Shouldn't these extensions be kept elsewhere? What about CompilerConfiguration?
+ // This method probably shouldn't be in GroovyMain either.
+ String[] standardExtensions = {".groovy",".gvy",".gy",".gsh"};
+ int i = 0;
+ while (i < standardExtensions.length && !scriptFile.exists()) {
+ scriptFile = new File(scriptFileName + standardExtensions[i]);
+ i++;
+ }
+ // if we still haven't found the file, point back to the originally specified filename
+ if (!scriptFile.exists()) {
+ scriptFile = new File(scriptFileName);
+ }
+ return scriptFile;
+ }
+
+ /**
+ * Hunt for the script file by calling searchForGroovyScriptFile(String).
+ *
+ * @see GroovyMain#searchForGroovyScriptFile(String)
+ */
+ public File huntForTheScriptFile(String input) {
+ return GroovyMain.searchForGroovyScriptFile(input);
+ }
+
+ // GROOVY-6771
+ private static void setupContextClassLoader(GroovyShell shell) {
+ final Thread current = Thread.currentThread();
+ class DoSetContext implements PrivilegedAction {
+ ClassLoader classLoader;
+
+ public DoSetContext(ClassLoader loader) {
+ classLoader = loader;
+ }
+
+ public Object run() {
+ current.setContextClassLoader(classLoader);
+ return null;
+ }
+ }
+
+ AccessController.doPrivileged(new DoSetContext(shell.getClassLoader()));
+ }
+
+ /**
+ * Process the input files.
+ */
+ private void processFiles() throws CompilationFailedException, IOException, URISyntaxException {
+ GroovyShell groovy = new GroovyShell(conf);
+ setupContextClassLoader(groovy);
+
+ Script s = groovy.parse(getScriptSource(isScriptFile, script));
+
+ if (args.isEmpty()) {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+ PrintWriter writer = new PrintWriter(System.out);
+
+ try {
+ processReader(s, reader, writer);
+ } finally {
+ reader.close();
+ writer.close();
+ }
+
+ } else {
+ Iterator i = args.iterator();
+ while (i.hasNext()) {
+ String filename = (String) i.next();
+ //TODO: These are the arguments for -p and -i. Why are we searching using Groovy script extensions?
+ // Where is this documented?
+ File file = huntForTheScriptFile(filename);
+ processFile(s, file);
+ }
+ }
+ }
+
+ /**
+ * Process a single input file.
+ *
+ * @param s the script to execute.
+ * @param file the input file.
+ */
+ private void processFile(Script s, File file) throws IOException {
+ if (!file.exists())
+ throw new FileNotFoundException(file.getName());
+
+ if (!editFiles) {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ try {
+ PrintWriter writer = new PrintWriter(System.out);
+ processReader(s, reader, writer);
+ writer.flush();
+ } finally {
+ reader.close();
+ }
+ } else {
+ File backup;
+ if (backupExtension == null) {
+ backup = File.createTempFile("groovy_", ".tmp");
+ backup.deleteOnExit();
+ } else {
+ backup = new File(file.getPath() + backupExtension);
+ }
+ backup.delete();
+ if (!file.renameTo(backup))
+ throw new IOException("unable to rename " + file + " to " + backup);
+
+ BufferedReader reader = new BufferedReader(new FileReader(backup));
+ try {
+ PrintWriter writer = new PrintWriter(new FileWriter(file));
+ try {
+ processReader(s, reader, writer);
+ } finally {
+ writer.close();
+ }
+ } finally {
+ reader.close();
+ }
+ }
+ }
+
+ /**
+ * Process a script against a single input file.
+ *
+ * @param s script to execute.
+ * @param reader input file.
+ * @param pw output sink.
+ */
+ private void processReader(Script s, BufferedReader reader, PrintWriter pw) throws IOException {
+ String line;
+ String lineCountName = "count";
+ s.setProperty(lineCountName, BigInteger.ZERO);
+ String autoSplitName = "split";
+ s.setProperty("out", pw);
+
+ try {
+ InvokerHelper.invokeMethod(s, "begin", null);
+ } catch (MissingMethodException mme) {
+ // ignore the missing method exception
+ // as it means no begin() method is present
+ }
+
+ while ((line = reader.readLine()) != null) {
+ s.setProperty("line", line);
+ s.setProperty(lineCountName, ((BigInteger)s.getProperty(lineCountName)).add(BigInteger.ONE));
+
+ if(autoSplit) {
+ s.setProperty(autoSplitName, line.split(splitPattern));
+ }
+
+ Object o = s.run();
+
+ if (autoOutput && o != null) {
+ pw.println(o);
+ }
+ }
+
+ try {
+ InvokerHelper.invokeMethod(s, "end", null);
+ } catch (MissingMethodException mme) {
+ // ignore the missing method exception
+ // as it means no end() method is present
+ }
+ }
+
+ /**
+ * Process the standard, single script with args.
+ */
+ private void processOnce() throws CompilationFailedException, IOException, URISyntaxException {
+ GroovyShell groovy = new GroovyShell(conf);
+ setupContextClassLoader(groovy);
+ groovy.run(getScriptSource(isScriptFile, script), args);
+ }
+}