blob: 88b8ee19015ef8584513d73762db1aaa028ea1f5 [file] [log] [blame]
/*
* Copyright 2008-2013 the original author or authors.
*
* Licensed 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.transform.GroovyASTTransformationClass;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Field annotation to automatically delegate part of the functionality of an owner class to the annotated field.
* <p>
* All public instance methods present in the type of the annotated field and not present in the owner class
* will be added to owner class at compile time. The implementation of such automatically added
* methods is code which calls through to the delegate as per the normal delegate pattern.
* <p>
* As an example, consider this code:
* <pre>
* class Event {
* {@code @Delegate} Date when
* String title, url
* }
*
* def gr8conf = new Event(title: "GR8 Conference",
* url: "http://www.gr8conf.org",
* when: Date.parse("yyyy/MM/dd", "2009/05/18"))
*
* def javaOne = new Event(title: "JavaOne",
* url: "http://java.sun.com/javaone/",
* when: Date.parse("yyyy/MM/dd", "2009/06/02"))
*
* assert gr8conf.before(javaOne.when)
* </pre>
*
* In this example, the {@code Event} class will have a method called
* {@code before(Date otherDate)} as well as other public methods of the
* {@code Date} class.
* The implementation of the {@code before()} method will look like this:
* <pre>
* public boolean before(Date otherDate) {
* return when.before(otherDate);
* }
* </pre>
*
* By default, the owner class will also be modified to implement any interfaces
* implemented by the field. So, in the example above, because {@code Date}
* implements {@code Cloneable} the following will be true:
*
* <pre>
* assert gr8conf instanceof Cloneable
* </pre>
*
* This behavior can be disabled by setting the
* annotation's {@code interfaces} element to false,
* i.e. {@code @Delegate(interfaces = false)}, e.g. in the above
* example, the delegate definition would become:
* <pre>
* {@code @Delegate}(interfaces = false) Date when
* </pre>
* and the following would be true:
* <pre>
* assert !(gr8conf instanceof Cloneable)
* </pre>
*
* If multiple delegate fields are used and the same method signature occurs
* in more than one of the respective field types, then the delegate will be
* made to the first defined field having that signature. If this does occur,
* it might be regarded as a smell (or at least poor style) and it might be
* clearer to do the delegation by long hand.
* <p>
* By default, methods of the delegate type marked as {@code @Deprecated} are
* not automatically added to the owner class (but see the technical note
* about interfaces below). You can force these methods to
* be added by setting the annotation's {@code deprecated} element to true,
* i.e. {@code @Delegate(deprecated = true)}.
* <p>
* For example, in the example above if we change the delegate definition to:
* <pre>
* {@code @Delegate}(deprecated = true) Date when
* </pre>
* then the following additional lines will execute successfully (during 2009):
* <pre>
* assert gr8conf.year + 1900 == 2009
* assert gr8conf.toGMTString().contains(" 2009 ")
* </pre>
* Otherwise these lines produce a groovy.lang.MissingPropertyException
* or groovy.lang.MissingMethodException respectively as those two methods are
* {@code @Deprecated} in {@code Date}.
* <p>
* <b>Technical notes</b>:
* <ul>
* <li>Static methods, synthetic methods or methods from the <code>GroovyObject</code> interface
* are not candidates for delegation</li>
* <li>Non-abstract non-static methods defined in the owner class or its superclasses take
* precedence over methods with identical signatures from a {@code @Delegate} field</li>
* <li>All methods defined in the owner class (including static, abstract or private etc.)
* take precedence over methods with identical signatures from a {@code @Delegate} field</li>
* <li>Recursive delegation to your own class is not allowed</li>
* <li>Mixing of {@code @Delegate} with default method arguments is known not to work in some cases.
* We recommend not using these features together.</li>
* <li>When the type of the delegate field is an interface, the {@code deprecated} attribute will be
* ignored if the owner class implements that interface (i.e. you must set {@code interfaces=false}
* if you want the {@code deprecated} attribute to be used). Otherwise, the resulting class would
* not compile anyway without manually adding in any deprecated methods in the interface.</li>
* </ul>
*
* @author Alex Tkachman
* @author Paul King
*/
@java.lang.annotation.Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD})
@GroovyASTTransformationClass("org.codehaus.groovy.transform.DelegateASTTransformation")
public @interface Delegate {
/**
* @return true if owner class should implement interfaces implemented by field
*/
boolean interfaces() default true;
/**
* Whether to apply the delegate pattern to deprecated methods; to avoid compilation
* errors, this is ignored if the type of the delegate field is an interface and
* {@code interfaces=true}.
*
* @return true if owner class should delegate to methods annotated with @Deprecated
*/
boolean deprecated() default false;
/**
* Whether to carry over annotations from the methods of the delegate
* to your delegating method. Currently Closure annotation members are
* not supported.
*
* @return true if generated delegate methods should keep method annotations
*/
boolean methodAnnotations() default false;
/**
* Whether to carry over annotations from the parameters of delegate
* methods to your delegating method. Currently Closure annotation members are
* not supported.
*
* @return true if generated delegate methods should keep parameter annotations
*/
boolean parameterAnnotations() default false;
}