Java tutorial
/* * 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 org.apache.groovy.ast.tools.AnnotatedNodeUtils; import org.apache.groovy.ast.tools.MethodNodeUtils; 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.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.VariableExpression; import org.codehaus.groovy.ast.tools.BeanUtils; import org.codehaus.groovy.ast.tools.GeneralUtils; import org.codehaus.groovy.ast.tools.GenericsUtils; import org.codehaus.groovy.control.SourceUnit; import org.codehaus.groovy.control.messages.SyntaxErrorMessage; import org.codehaus.groovy.runtime.StringGroovyMethods; import org.codehaus.groovy.syntax.SyntaxException; import org.objectweb.asm.Opcodes; import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import static groovy.transform.Undefined.isUndefined; import static org.codehaus.groovy.ast.tools.GeneralUtils.getInstanceNonPropertyFieldNames; import static org.codehaus.groovy.ast.tools.GeneralUtils.getSuperNonPropertyFields; public abstract class AbstractASTTransformation implements Opcodes, ASTTransformation, ErrorCollecting { public static final ClassNode RETENTION_CLASSNODE = ClassHelper.makeWithoutCaching(Retention.class); protected SourceUnit sourceUnit; /** * Copies all <tt>candidateAnnotations</tt> with retention policy {@link java.lang.annotation.RetentionPolicy#RUNTIME} * and {@link java.lang.annotation.RetentionPolicy#CLASS}. * <p> * Annotations with {@link org.codehaus.groovy.runtime.GeneratedClosure} members are not supported for now. */ protected List<AnnotationNode> copyAnnotatedNodeAnnotations(final AnnotatedNode annotatedNode, String myTypeName) { return copyAnnotatedNodeAnnotations(annotatedNode, myTypeName, true); } /** * Copies all <tt>candidateAnnotations</tt> with retention policy {@link java.lang.annotation.RetentionPolicy#RUNTIME} * and {@link java.lang.annotation.RetentionPolicy#CLASS}. * <p> * Annotations with {@link org.codehaus.groovy.runtime.GeneratedClosure} members are not supported for now. */ protected List<AnnotationNode> copyAnnotatedNodeAnnotations(final AnnotatedNode annotatedNode, String myTypeName, boolean includeGenerated) { final List<AnnotationNode> copiedAnnotations = new ArrayList<>(); final List<AnnotationNode> notCopied = new ArrayList<>(); GeneralUtils.copyAnnotatedNodeAnnotations(annotatedNode, copiedAnnotations, notCopied, includeGenerated); for (AnnotationNode annotation : notCopied) { addError(myTypeName + " does not support keeping Closure annotation members.", annotation); } return copiedAnnotations; } /** * If the transform is associated with a single annotation, returns a name suitable for displaying in error messages. * * @return The simple name of the annotation including the "@" or null if no such name is defined */ public String getAnnotationName() { return null; } protected void init(ASTNode[] nodes, SourceUnit sourceUnit) { if (nodes == null || nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) { throw new GroovyBugError("Internal error: expecting [AnnotationNode, AnnotatedNode] but got: " + (nodes == null ? null : Arrays.asList(nodes))); } this.sourceUnit = sourceUnit; } public boolean memberHasValue(AnnotationNode node, String name, Object value) { final Expression member = node.getMember(name); return member instanceof ConstantExpression && ((ConstantExpression) member).getValue().equals(value); } public Object getMemberValue(AnnotationNode node, String name) { final Expression member = node.getMember(name); if (member instanceof ConstantExpression) return ((ConstantExpression) member).getValue(); return null; } public static String getMemberStringValue(AnnotationNode node, String name, String defaultValue) { final Expression member = node.getMember(name); if (member instanceof ConstantExpression) { Object result = ((ConstantExpression) member).getValue(); if (result instanceof String && isUndefined((String) result)) result = null; if (result != null) return result.toString(); } return defaultValue; } public static String getMemberStringValue(AnnotationNode node, String name) { return getMemberStringValue(node, name, null); } public int getMemberIntValue(AnnotationNode node, String name) { Object value = getMemberValue(node, name); if (value instanceof Integer) { return (Integer) value; } return 0; } public ClassNode getMemberClassValue(AnnotationNode node, String name) { return getMemberClassValue(node, name, null); } public ClassNode getMemberClassValue(AnnotationNode node, String name, ClassNode defaultValue) { final Expression member = node.getMember(name); if (member != null) { if (member instanceof ClassExpression) { if (!isUndefined(member.getType())) return member.getType(); } else if (member instanceof VariableExpression) { addError("Error expecting to find class value for '" + name + "' but found variable: " + member.getText() + ". Missing import?", node); return null; } else if (member instanceof ConstantExpression) { addError("Error expecting to find class value for '" + name + "' but found constant: " + member.getText() + "!", node); return null; } } return defaultValue; } public static List<String> getMemberStringList(AnnotationNode anno, String name) { Expression expr = anno.getMember(name); if (expr == null) { return null; } if (expr instanceof ListExpression) { final ListExpression listExpression = (ListExpression) expr; if (isUndefinedMarkerList(listExpression)) { return null; } return getValueStringList(listExpression); } return tokenize(getMemberStringValue(anno, name)); } private static boolean isUndefinedMarkerList(ListExpression listExpression) { if (listExpression.getExpressions().size() != 1) return false; Expression itemExpr = listExpression.getExpression(0); if (itemExpr == null) return false; if (itemExpr instanceof ConstantExpression) { Object value = ((ConstantExpression) itemExpr).getValue(); if (value instanceof String && isUndefined((String) value)) return true; } else if (itemExpr instanceof ClassExpression && isUndefined(itemExpr.getType())) { return true; } return false; } private static List<String> getValueStringList(ListExpression listExpression) { List<String> list = new ArrayList<>(); for (Expression itemExpr : listExpression.getExpressions()) { if (itemExpr instanceof ConstantExpression) { Object value = ((ConstantExpression) itemExpr).getValue(); if (value != null) list.add(value.toString()); } } return list; } public List<ClassNode> getMemberClassList(AnnotationNode anno, String name) { List<ClassNode> list = new ArrayList<>(); Expression expr = anno.getMember(name); if (expr == null) { return null; } if (expr instanceof ListExpression) { final ListExpression listExpression = (ListExpression) expr; if (isUndefinedMarkerList(listExpression)) { return null; } list = getTypeList(listExpression); } else if (expr instanceof ClassExpression) { ClassNode cn = expr.getType(); if (isUndefined(cn)) return null; if (cn != null) list.add(cn); } return list; } private static List<ClassNode> getTypeList(ListExpression listExpression) { List<ClassNode> list = new ArrayList<>(); for (Expression itemExpr : listExpression.getExpressions()) { if (itemExpr instanceof ClassExpression) { ClassNode cn = itemExpr.getType(); if (cn != null) list.add(cn); } } return list; } public void addError(String msg, ASTNode expr) { sourceUnit.getErrorCollector() .addErrorAndContinue(new SyntaxErrorMessage(new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber(), expr.getLastLineNumber(), expr.getLastColumnNumber()), sourceUnit)); } protected boolean checkNotInterface(ClassNode cNode, String annotationName) { if (cNode.isInterface()) { addError("Error processing interface '" + cNode.getName() + "'. " + annotationName + " not allowed for interfaces.", cNode); return false; } return true; } public boolean hasAnnotation(ClassNode node, ClassNode annotation) { return AnnotatedNodeUtils.hasAnnotation(node, annotation); } public static List<String> tokenize(String rawExcludes) { return rawExcludes == null ? new ArrayList<>() : StringGroovyMethods.tokenize(rawExcludes, ", "); } public static boolean deemedInternalName(String name) { return name.contains("$"); } public static boolean shouldSkipUndefinedAware(String name, List<String> excludes, List<String> includes) { return shouldSkipUndefinedAware(name, excludes, includes, false); } public static boolean shouldSkipUndefinedAware(String name, List<String> excludes, List<String> includes, boolean allNames) { return (excludes != null && excludes.contains(name)) || (!allNames && deemedInternalName(name)) || (includes != null && !includes.contains(name)); } public static boolean shouldSkip(String name, List<String> excludes, List<String> includes) { return shouldSkip(name, excludes, includes, false); } public static boolean shouldSkip(String name, List<String> excludes, List<String> includes, boolean allNames) { return (excludes != null && excludes.contains(name)) || (!allNames && deemedInternalName(name)) || (includes != null && !includes.isEmpty() && !includes.contains(name)); } public static boolean shouldSkipOnDescriptorUndefinedAware(boolean checkReturn, Map genericsSpec, MethodNode mNode, List<ClassNode> excludeTypes, List<ClassNode> includeTypes) { String descriptor = mNode.getTypeDescriptor(); String descriptorNoReturn = MethodNodeUtils.methodDescriptorWithoutReturnType(mNode); if (excludeTypes != null) { for (ClassNode cn : excludeTypes) { List<ClassNode> remaining = new LinkedList<>(); remaining.add(cn); Map updatedGenericsSpec = new HashMap(genericsSpec); while (!remaining.isEmpty()) { ClassNode next = remaining.remove(0); if (!next.equals(ClassHelper.OBJECT_TYPE)) { updatedGenericsSpec = GenericsUtils.createGenericsSpec(next, updatedGenericsSpec); for (MethodNode mn : next.getMethods()) { MethodNode correctedMethodNode = GenericsUtils .correctToGenericsSpec(updatedGenericsSpec, mn); if (checkReturn) { String md = correctedMethodNode.getTypeDescriptor(); if (md.equals(descriptor)) return true; } else { String md = MethodNodeUtils.methodDescriptorWithoutReturnType(correctedMethodNode); if (md.equals(descriptorNoReturn)) return true; } } remaining.addAll(Arrays.asList(next.getInterfaces())); } } } } if (includeTypes == null) return false; for (ClassNode cn : includeTypes) { List<ClassNode> remaining = new LinkedList<>(); remaining.add(cn); Map updatedGenericsSpec = new HashMap(genericsSpec); while (!remaining.isEmpty()) { ClassNode next = remaining.remove(0); if (!next.equals(ClassHelper.OBJECT_TYPE)) { updatedGenericsSpec = GenericsUtils.createGenericsSpec(next, updatedGenericsSpec); for (MethodNode mn : next.getMethods()) { MethodNode correctedMethodNode = GenericsUtils.correctToGenericsSpec(updatedGenericsSpec, mn); if (checkReturn) { String md = correctedMethodNode.getTypeDescriptor(); if (md.equals(descriptor)) return false; } else { String md = MethodNodeUtils.methodDescriptorWithoutReturnType(correctedMethodNode); if (md.equals(descriptorNoReturn)) return false; } } remaining.addAll(Arrays.asList(next.getInterfaces())); } } } return true; } protected boolean checkIncludeExcludeUndefinedAware(AnnotationNode node, List<String> excludes, List<String> includes, String typeName) { if (includes != null && excludes != null && !excludes.isEmpty()) { addError( "Error during " + typeName + " processing: Only one of 'includes' and 'excludes' should be supplied not both.", node); return false; } return true; } protected void checkIncludeExcludeUndefinedAware(AnnotationNode node, List<String> excludes, List<String> includes, List<ClassNode> excludeTypes, List<ClassNode> includeTypes, String typeName) { int found = 0; if (includes != null) found++; if (excludes != null && !excludes.isEmpty()) found++; if (includeTypes != null) found++; if (excludeTypes != null && !excludeTypes.isEmpty()) found++; if (found > 1) { addError("Error during " + typeName + " processing: Only one of 'includes', 'excludes', 'includeTypes' and 'excludeTypes' should be supplied.", node); } } public boolean checkPropertyList(ClassNode cNode, List<String> propertyNameList, String listName, AnnotationNode anno, String typeName, boolean includeFields) { return checkPropertyList(cNode, propertyNameList, listName, anno, typeName, includeFields, false, false); } public boolean checkPropertyList(ClassNode cNode, List<String> propertyNameList, String listName, AnnotationNode anno, String typeName, boolean includeFields, boolean includeSuperProperties, boolean allProperties) { return checkPropertyList(cNode, propertyNameList, listName, anno, typeName, includeFields, includeSuperProperties, allProperties, false, false); } public boolean checkPropertyList(ClassNode cNode, List<String> propertyNameList, String listName, AnnotationNode anno, String typeName, boolean includeFields, boolean includeSuperProperties, boolean allProperties, boolean includeSuperFields, boolean includeStatic) { if (propertyNameList == null || propertyNameList.isEmpty()) { return true; } final List<String> pNames = new ArrayList<>(); for (PropertyNode pNode : BeanUtils.getAllProperties(cNode, includeSuperProperties, includeStatic, allProperties)) { pNames.add(pNode.getField().getName()); } boolean result = true; if (includeFields || includeSuperFields) { final List<String> fNames = new ArrayList<>(); if (includeFields) { fNames.addAll(getInstanceNonPropertyFieldNames(cNode)); } if (includeSuperFields) { List<FieldNode> superNonPropertyFields = getSuperNonPropertyFields(cNode.getSuperClass()); for (FieldNode fn : superNonPropertyFields) { fNames.add(fn.getName()); } } for (String pName : propertyNameList) { if (!pNames.contains(pName) && !fNames.contains(pName)) { addError("Error during " + typeName + " processing: '" + listName + "' property or field '" + pName + "' does not exist.", anno); result = false; } } } else { for (String pName : propertyNameList) { if (!pNames.contains(pName)) { addError("Error during " + typeName + " processing: '" + listName + "' property '" + pName + "' does not exist.", anno); result = false; } } } return result; } }