All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.groovy.transform.PackageScopeASTTransformation Maven / Gradle / Ivy

There is a newer version: 5.0.0-alpha-11
Show newest version
/*
 *  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 org.codehaus.groovy.transform;

import groovy.transform.PackageScope;
import groovy.transform.PackageScopeTarget;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotatedNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.ListExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.control.CompilePhase;
import org.codehaus.groovy.control.SourceUnit;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;

/**
 * Handles transformation for the @PackageScope annotation.
 * 

* Both the deprecated groovy.lang.PackageScope and groovy.transform.PackageScope * annotations are supported. The former will be removed in a future version of Groovy. */ @GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) public class PackageScopeASTTransformation extends AbstractASTTransformation { private static final Class MY_CLASS = PackageScope.class; private static final ClassNode MY_TYPE = ClassHelper.make(MY_CLASS); private static final String MY_TYPE_NAME = "@" + MY_TYPE.getNameWithoutPackage(); private static final String LEGACY_TYPE_NAME = "groovy.lang.PackageScope"; private static final Class TARGET_CLASS = groovy.transform.PackageScopeTarget.class; private static final String TARGET_CLASS_NAME = ClassHelper.make(TARGET_CLASS).getNameWithoutPackage(); @Override public void visit(ASTNode[] nodes, SourceUnit source) { init(nodes, source); AnnotatedNode parent = (AnnotatedNode) nodes[1]; AnnotationNode node = (AnnotationNode) nodes[0]; boolean legacyMode = LEGACY_TYPE_NAME.equals(node.getClassNode().getName()); if (!MY_TYPE.equals(node.getClassNode()) && !legacyMode) return; Expression value = node.getMember("value"); if (parent instanceof ClassNode) { List targets; if (value == null) targets = Collections.singletonList(legacyMode ? PackageScopeTarget.FIELDS : PackageScopeTarget.CLASS); else targets = determineTargets(value); visitClassNode((ClassNode) parent, targets); parent.getAnnotations(); } else { if (value != null) { addError("Error during " + MY_TYPE_NAME + " processing: " + TARGET_CLASS_NAME + " only allowed at class level.", parent); return; } if (parent instanceof MethodNode) { visitMethodNode((MethodNode) parent); } else if (parent instanceof FieldNode) { visitFieldNode((FieldNode) parent); } } } private void visitMethodNode(MethodNode methodNode) { if (methodNode.isSyntheticPublic()) revertVisibility(methodNode); else addError("Can't use " + MY_TYPE_NAME + " for method '" + methodNode.getName() + "' which has explicit visibility.", methodNode); } private void visitClassNode(ClassNode cNode, List value) { String cName = cNode.getName(); if (cNode.isInterface() && value.size() != 1 && value.get(0) != PackageScopeTarget.CLASS) { addError("Error processing interface '" + cName + "'. " + MY_TYPE_NAME + " not allowed for interfaces except when targeting Class level.", cNode); } if (value.contains(groovy.transform.PackageScopeTarget.CLASS)) { if (cNode.isSyntheticPublic()) revertVisibility(cNode); else addError("Can't use " + MY_TYPE_NAME + " for class '" + cNode.getName() + "' which has explicit visibility.", cNode); } if (value.contains(groovy.transform.PackageScopeTarget.METHODS)) { final List mList = cNode.getMethods(); for (MethodNode mNode : mList) { if (mNode.isSyntheticPublic()) revertVisibility(mNode); } } if (value.contains(groovy.transform.PackageScopeTarget.CONSTRUCTORS)) { final List cList = cNode.getDeclaredConstructors(); for (MethodNode mNode : cList) { if (mNode.isSyntheticPublic()) revertVisibility(mNode); } } if (value.contains(PackageScopeTarget.FIELDS)) { final List pList = cNode.getProperties(); List foundProps = new ArrayList(); List foundNames = new ArrayList(); for (PropertyNode pNode : pList) { foundProps.add(pNode); foundNames.add(pNode.getName()); } for (PropertyNode pNode : foundProps) { pList.remove(pNode); } final List fList = cNode.getFields(); for (FieldNode fNode : fList) { if (foundNames.contains(fNode.getName())) { revertVisibility(fNode); } } } } private void visitFieldNode(FieldNode fNode) { final ClassNode cNode = fNode.getDeclaringClass(); final List pList = cNode.getProperties(); PropertyNode foundProp = null; String fName = fNode.getName(); for (PropertyNode pNode : pList) { if (pNode.getName().equals(fName) && pNode.getAnnotations(MY_TYPE) != null) { foundProp = pNode; break; } } if (foundProp != null) { revertVisibility(fNode); pList.remove(foundProp); // now check for split property foundProp = null; for (PropertyNode pNode : pList) { if (pNode.getName().equals(fName)) { foundProp = pNode; break; } } if (foundProp != null) { FieldNode oldField = foundProp.getField(); cNode.getFields().remove(oldField); cNode.getFieldIndex().put(fName, fNode); if (foundProp.hasInitialExpression()) { if (fNode.hasInitialExpression()) { addError("The split property definition named '" + fName + "' must not have an initial value for both the field and the property", fNode); } fNode.setInitialValueExpression(foundProp.getInitialExpression()); } foundProp.setField(fNode); } } } private static void revertVisibility(FieldNode fNode) { fNode.setModifiers(fNode.getModifiers() & ~ACC_PRIVATE); fNode.setSynthetic(false); } private static void revertVisibility(MethodNode mNode) { mNode.setModifiers(mNode.getModifiers() & ~ACC_PUBLIC); } private static void revertVisibility(ClassNode cNode) { cNode.setModifiers(cNode.getModifiers() & ~ACC_PUBLIC); } private static List determineTargets(Expression expr) { List list = new ArrayList(); if (expr instanceof PropertyExpression) { list.add(extractTarget((PropertyExpression) expr)); } else if (expr instanceof ListExpression) { final ListExpression expressionList = (ListExpression) expr; final List expressions = expressionList.getExpressions(); for (Expression ex : expressions) { if (ex instanceof PropertyExpression) { list.add(extractTarget((PropertyExpression) ex)); } } } return list; } private static groovy.transform.PackageScopeTarget extractTarget(PropertyExpression expr) { Expression oe = expr.getObjectExpression(); if (oe instanceof ClassExpression) { ClassExpression ce = (ClassExpression) oe; if (ce.getType().getName().equals("groovy.transform.PackageScopeTarget")) { Expression prop = expr.getProperty(); if (prop instanceof ConstantExpression) { String propName = (String) ((ConstantExpression) prop).getValue(); try { return PackageScopeTarget.valueOf(propName); } catch(IllegalArgumentException iae) { /* ignore */ } } } } throw new GroovyBugError("Internal error during " + MY_TYPE_NAME + " processing. Annotation parameters must be of type: " + TARGET_CLASS_NAME + "."); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy