/* * Copyright 2008-2009 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; /** * Transforms an instance-style Groovy class or interface to become a static-style * conventional Groovy category. * </p> * Groovy categories are the original mechanism used * by Groovy when augmenting classes with new methods. Writing categories required * using a class writing style where all methods were static and an additional * self parameter was defined. The self parameter and static nature of the methods * disappeared once applied by Groovy's metaclass framework but some regarded * the writing style as a little noisy. This transformation allows you to write * your categories without the "apparent noise" but adds it back in during * compilation so that the classes appear as normal categories. * </p> * It might seem strange writing your class/object enhancements using a succinct * notation, then having "noise" added, then having the noise removed during * category application. If this worries you, then you may also like to consider * using Groovy's {@code ExpandoMetaClass} mechanism which avoids * the category definition altogether. If you already have an investment in * categories or like some of the other features which categories currently give you, * then read on. * </p> * The mechanics: during compilation, all methods are transformed to static ones with an additional * self parameter of the type you supply as the annotation parameter (the default type * for the self parameters is {@code Object} which might be more broad reaching than * you like so it is usually wise to specify a type). * Properties invoked using 'this' references are transformed so that * they are instead invoked on the additional self parameter and not on * the Category instance. (Remember that once the category is applied, the reverse * will occur and we will be back to conceptually having methods on the {@code this} * references again!) * </p> * Classes conforming to the conventional Groovy category conventions can be used * within {@code use} statements or mixed in at compile time with the {@code @Mixin} * transformation or at runtime with the {@code mixin} method on classes. * </p> * An example showing a {@code use} statement (allowing fine-grained application of * the category methods): * <pre> * {@code @Category}(Integer) * class IntegerOps { * def triple() { * this * 3 * } * } * * use (IntegerOps) { * assert 25.triple() == 75 * } * </pre> * Or, using the {@code @Mixin} flavor for compile-time "mixing in" of the methods: * <pre> * {@code @Category}(List) * class Shuffler { * def shuffle() { * def result = new ArrayList(this) * Collections.shuffle(result) * result * } * } * * {@code @Mixin}(Shuffler) * class Sentence extends ArrayList { * Sentence(Collection initial) { super(initial) } * } * * def words = ["The", "quick", "brown", "fox"] * println new Sentence(words).shuffle() * // => [quick, fox, The, brown] (order will vary) * </pre> * Or, instead of using {@code @Mixin}, try "mixing in" your methods at runtime: * <pre> * // ... as before ... * * class Sentence extends ArrayList { * Sentence(Collection initial) { super(initial) } * } * Sentence.mixin Shuffler * * // ... as before ... * </pre> * * @author Alex Tkachman */ @java.lang.annotation.Documented @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) @GroovyASTTransformationClass("org.codehaus.groovy.transform.CategoryASTTransformation") public @interface Category { Class value () default Object.class; }