blob: 658dda7751445cb63757799526a6a8422d4b9ad6 [file] [log] [blame]
/*
* 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;
import org.apache.groovy.lang.annotation.Incubating;
import org.codehaus.groovy.transform.GroovyASTTransformationClass;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Allows construction of a named-arg equivalent method or constructor.
* The method or constructor will have at least a first argument of type
* {@code Map} and may have more arguments. As such, it can be called
* using Groovy's named-arg syntax. The original method/constructor is retained
* and is called by the generated method/constructor.
*
* One benefit of this approach is the potential for improved type checking.
* The annotated "tuple" method/constructor can be type rich and will be checked
* as such during normal compilation. The generated method/constructor using
* the map argument will be named-argument friendly but the map also hides
* type information. The generated method however contains no business logic
* so the chance of errors is minimal.
*
* Any arguments identified as named arguments will be supplied as part of the map.
* Any additional arguments are supplied in the normal tuple style.
*
* Named parameters are identified in one of three ways:
* <ol>
* <li>Use one or more {@code @NamedParam} annotations to explicitly identify such parameters</li>
* <li>Use one or more {@code @NamedDelegate} annotations to explicitly identify such parameters as
* delegate parameters</li>
* <li>If no parameters with {@code @NamedParam} or {@code @NamedDelegate} annotations are found then:
* <ul>
* <li>If {@code autoDelegate} is false (the default), all parameters are treated as if they were named parameters</li>
* <li>If {@code autoDelegate} is true, the first parameter is treated as if it is a delegate parameter</li>
* </ul>
* </li>
* </ol>
* You can also mix and match the {@code @NamedParam} and {@code @NamedDelegate} annotations.
*
* Named arguments will be supplied via the map with their property name (configurable via
* annotation attributes within {@code @NamedParam}) being the key and value being the argument value.
* For named delegates, any properties of the delegate can become map keys.
* Duplicate keys across delegate properties or named parameters are not allowed.
* The type of delegate parameters must be compatible with Groovy's {@code as} cast operation from a {@code Map}.
*
* Here is an example using implicit named parameters.
* <pre class="groovyTestCase">
* import groovy.transform.*
*
* {@code @NamedVariant}
* int makeSense(int dollars, int cents) {
* 100 * dollars + cents
* }
*
* assert makeSense(dollars: 2, cents: 50) == 250
* </pre>
* Here is an example using a delegate parameter.
* <pre class="groovyTestCase">
* import groovy.transform.*
*
* {@code @ToString(includeNames=true)}
* class Color {
* Integer r, g, b
* }
*
* {@code @NamedVariant}
* String foo({@code @NamedDelegate} Color shade) {
* shade
* }
*
* def result = foo(g: 12, b: 42, r: 12)
* assert result.toString() == 'Color(r:12, g:12, b:42)'
* </pre>
* You could also explicitly annotate the {@code shade} argument with the {@code @NamedDelegate} annotation if you wanted.
*
* The generated method will be something like this:
* <pre>
* String foo(Map args) {
* return foo(args as Color)
* }
* </pre>
* The generated method/constructor retains the visibility and return type of the original method/constructor
* but the {@link VisibilityOptions} annotation can be added to customize the visibility. You could have the
* annotated method/constructor private for instance but have the generated one be public.
*
* @see VisibilityOptions
* @see NamedParam
* @see NamedDelegate
* @since 2.5.0
*/
@Incubating
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
@GroovyASTTransformationClass("org.codehaus.groovy.transform.NamedVariantASTTransformation")
public @interface NamedVariant {
/**
* If specified, must match the optional "id" attribute in an applicable {@code VisibilityOptions} annotation.
*/
String visibilityId() default Undefined.STRING;
/**
* If true, add an implicit {@code @NamedDelegate} to the first parameter if no {@code @NamedDelegate} or {@code @NamedParam} annotations are found on any parameter.
*
* @since 2.5.3
*/
boolean autoDelegate() default false;
/**
* If true, will use {@code as} to convert map parameter to required class
*
* @since 3.0.6
*/
boolean coerce() default false;
}