[ https://issues.apache.org/jira/browse/GROOVY-10801?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17626081#comment-17626081 ]
Eric Milles commented on GROOVY-10801: -------------------------------------- If this idea has merit, I have a basic implementation to try out: {code:groovy} import static org.apache.groovy.ast.tools.ClassNodeUtils.addGeneratedConstructor import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX import static org.codehaus.groovy.ast.tools.GeneralUtils.throwS import static org.codehaus.groovy.control.CompilePhase.SEMANTIC_ANALYSIS import java.lang.annotation.* import java.lang.reflect.Modifier import org.codehaus.groovy.ast.* import org.codehaus.groovy.control.SourceUnit import org.codehaus.groovy.transform.ASTTransformation import org.codehaus.groovy.transform.GroovyASTTransformation import org.codehaus.groovy.transform.GroovyASTTransformationClass @GroovyASTTransformationClass(classes = NamespaceTransform) @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) @interface Namespace { } //------------------------------------------------------------------------------ @AutoFinal @GroovyASTTransformation(phase = SEMANTIC_ANALYSIS) class NamespaceTransform implements ASTTransformation { @Override void visit(ASTNode[] nodes, SourceUnit source) { if (!nodes || !nodes[0] || !nodes[1]) return if (!(nodes[1] instanceof ClassNode)) return if (!(nodes[0] instanceof AnnotationNode)) return if (nodes[0].classNode?.name != Namespace.name) return try { ClassNode classNode = nodes[1] // check preconditions if ((classNode.modifiers & 0x6600) || classNode.methods.any{ it.isAbstract() }) // TODO: record, sealed source.addFatalError('Namespace cannot apply to abstract, interface, annotation, or enumeration', nodes[0]) if (classNode.declaredConstructors) source.addFatalError('Namespace cannot have explicit constructor(s)', classNode.declaredConstructors.first()) xform(classNode) } catch (e) { source.addException(e) } } private static void xform(ClassNode classNode) { // adjust modifiers classNode.modifiers = (classNode.modifiers & 0x807) | Modifier.FINAL if (classNode.outerClass) { classNode.removeField('this$0') // TODO: stop MOP method generation classNode.staticClass = true; classNode.modifiers |= Modifier.STATIC } // make fields static and final for (field in classNode.fields) { field.modifiers |= Modifier.FINAL | Modifier.STATIC if (field.isSynthetic()) { // property field.synthetic = false field.modifiers ^= Modifier.PUBLIC | Modifier.PRIVATE classNode.properties.removeIf { it.name == field.name } } } // make methods static for (method in classNode.methods) { method.modifiers |= Modifier.STATIC } // add private no-arg constructor addGeneratedConstructor(classNode, Modifier.PRIVATE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, throwS(ctorX(ClassHelper.make(AssertionError)))) // TODO: disable "implements GroovyObject" } } {code} I'd like to stop the addition of {{GroovyObject}} and {{getMetaClass}}/{{setMetaClass}} but it is somewhat baked into the compiler. I don't think they harm usability any. > AST transform for simple utility classes (only static fields and methods) > ------------------------------------------------------------------------- > > Key: GROOVY-10801 > URL: https://issues.apache.org/jira/browse/GROOVY-10801 > Project: Groovy > Issue Type: New Feature > Components: ast builder > Reporter: Eric Milles > Assignee: Eric Milles > Priority: Minor > > Similar to the {{@Category}} transform, I'd like to have a local transform > for utility classes. Consider the following: > {code:groovy} > @Namespace > class C { > int constant = 1 > def method() { > // ... > } > } > {code} > This would be Groovy shorthand for the canonical "utility class": > {code:groovy} > final class C { > private C() { throw new AssertionError() } > public static final int constant = 1 > static method { > // ... > } > } > {code} -- This message was sent by Atlassian Jira (v8.20.10#820010)