static List<Extension> getApplicableExtensionMethods( EclipseNode typeNode, Annotation ann, TypeBinding receiverType) { List<Extension> extensions = new ArrayList<Extension>(); if ((typeNode != null) && (ann != null) && (receiverType != null)) { BlockScope blockScope = ((TypeDeclaration) typeNode.get()).initializerScope; EclipseNode annotationNode = typeNode.getNodeFor(ann); AnnotationValues<ExtensionMethod> annotation = createAnnotation(ExtensionMethod.class, annotationNode); boolean suppressBaseMethods = false; try { suppressBaseMethods = annotation.getInstance().suppressBaseMethods(); } catch (AnnotationValueDecodeFail fail) { fail.owner.setError(fail.getMessage(), fail.idx); } for (Object extensionMethodProvider : annotation.getActualExpressions("value")) { if (extensionMethodProvider instanceof ClassLiteralAccess) { TypeBinding binding = ((ClassLiteralAccess) extensionMethodProvider).type.resolveType(blockScope); if (binding == null) continue; if (!binding.isClass() && !binding.isEnum()) continue; Extension e = new Extension(); e.extensionMethods = getApplicableExtensionMethodsDefinedInProvider( typeNode, (ReferenceBinding) binding, receiverType); e.suppressBaseMethods = suppressBaseMethods; extensions.add(e); } } } return extensions; }
@Override public void handle( AnnotationValues<EqualsAndHashCode> annotation, JCAnnotation ast, JavacNode annotationNode) { deleteAnnotationIfNeccessary(annotationNode, EqualsAndHashCode.class); EqualsAndHashCode ann = annotation.getInstance(); List<String> excludes = List.from(ann.exclude()); List<String> includes = List.from(ann.of()); JavacNode typeNode = annotationNode.up(); checkForBogusFieldNames(typeNode, annotation); Boolean callSuper = ann.callSuper(); if (!annotation.isExplicit("callSuper")) callSuper = null; if (!annotation.isExplicit("exclude")) excludes = null; if (!annotation.isExplicit("of")) includes = null; if (excludes != null && includes != null) { excludes = null; annotation.setWarning( "exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored."); } FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; generateMethods(typeNode, annotationNode, excludes, includes, callSuper, true, fieldAccess); }
public boolean handle( AnnotationValues<ToString> annotation, Annotation ast, EclipseNode annotationNode) { ToString ann = annotation.getInstance(); List<String> excludes = Arrays.asList(ann.exclude()); List<String> includes = Arrays.asList(ann.of()); EclipseNode typeNode = annotationNode.up(); Boolean callSuper = ann.callSuper(); if (!annotation.isExplicit("callSuper")) callSuper = null; if (!annotation.isExplicit("exclude")) excludes = null; if (!annotation.isExplicit("of")) includes = null; if (excludes != null && includes != null) { excludes = null; annotation.setWarning( "exclude", "exclude and of are mutually exclusive; the 'exclude' parameter will be ignored."); } checkForBogusFieldNames(typeNode, annotation); FieldAccess fieldAccess = ann.doNotUseGetters() ? FieldAccess.PREFER_FIELD : FieldAccess.GETTER; return generateToString( typeNode, annotationNode, excludes, includes, ann.includeFieldNames(), callSuper, true, fieldAccess); }
/** * When generating a setter, the setter either returns void (beanspec) or Self (fluent). This * method scans for the {@code Accessors} annotation to figure that out. */ public static boolean shouldReturnThis(JavacNode field) { if ((((JCVariableDecl) field.get()).mods.flags & Flags.STATIC) != 0) return false; AnnotationValues<Accessors> accessors = JavacHandlerUtil.getAccessorsForField(field); boolean forced = (accessors.getActualExpression("chain") != null); Accessors instance = accessors.getInstance(); return instance.chain() || (instance.fluent() && !forced); }
private static GetterMethod findGetter(JavacNode field) { JCVariableDecl decl = (JCVariableDecl) field.get(); JavacNode typeNode = field.up(); for (String potentialGetterName : toAllGetterNames(field)) { for (JavacNode potentialGetter : typeNode.down()) { if (potentialGetter.getKind() != Kind.METHOD) continue; JCMethodDecl method = (JCMethodDecl) potentialGetter.get(); if (!method.name.toString().equalsIgnoreCase(potentialGetterName)) continue; /** static getX() methods don't count. */ if ((method.mods.flags & Flags.STATIC) != 0) continue; /** Nor do getters with a non-empty parameter list. */ if (method.params != null && method.params.size() > 0) continue; return new GetterMethod(method.name, method.restype); } } // Check if the field has a @Getter annotation. boolean hasGetterAnnotation = false; for (JavacNode child : field.down()) { if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) { AnnotationValues<Getter> ann = createAnnotation(Getter.class, child); if (ann.getInstance().value() == AccessLevel.NONE) return null; // Definitely WONT have a getter. hasGetterAnnotation = true; } } // Check if the class has a @Getter annotation. if (!hasGetterAnnotation && new HandleGetter().fieldQualifiesForGetterGeneration(field)) { // Check if the class has @Getter or @Data annotation. JavacNode containingType = field.up(); if (containingType != null) for (JavacNode child : containingType.down()) { if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Data.class, child)) hasGetterAnnotation = true; if (child.getKind() == Kind.ANNOTATION && annotationTypeMatches(Getter.class, child)) { AnnotationValues<Getter> ann = createAnnotation(Getter.class, child); if (ann.getInstance().value() == AccessLevel.NONE) return null; // Definitely WONT have a getter. hasGetterAnnotation = true; } } } if (hasGetterAnnotation) { String getterName = toGetterName(field); if (getterName == null) return null; return new GetterMethod(field.toName(getterName), decl.vartype); } return null; }
static boolean lookForGetter(JavacNode field, FieldAccess fieldAccess) { if (fieldAccess == FieldAccess.GETTER) return true; if (fieldAccess == FieldAccess.ALWAYS_FIELD) return false; // If @Getter(lazy = true) is used, then using it is mandatory. for (JavacNode child : field.down()) { if (child.getKind() != Kind.ANNOTATION) continue; if (annotationTypeMatches(Getter.class, child)) { AnnotationValues<Getter> ann = createAnnotation(Getter.class, child); if (ann.getInstance().lazy()) return true; } } return false; }
@Override public void handle( final AnnotationValues<ListenerSupport> annotation, final JCAnnotation source, final JavacNode annotationNode) { deleteAnnotationIfNeccessary(annotationNode, ListenerSupport.class); JavacType type = JavacType.typeOf(annotationNode, source); if (type.isAnnotation() || type.isInterface()) { annotationNode.addError(canBeUsedOnClassAndEnumOnly(ListenerSupport.class)); return; } List<Object> listenerInterfaces = annotation.getActualExpressions("value"); if (listenerInterfaces.isEmpty()) { annotationNode.addError( String.format( "@%s has no effect since no interface types were specified.", ListenerSupport.class.getName())); return; } List<TypeSymbol> resolvedInterfaces = resolveInterfaces(annotationNode, ListenerSupport.class, listenerInterfaces); for (TypeSymbol interfaze : resolvedInterfaces) { handler.addListenerField(type, interfaze); handler.addAddListenerMethod(type, interfaze); handler.addRemoveListenerMethod(type, interfaze); addFireListenerMethods(type, interfaze); } type.editor().rebuild(); }
public <A extends java.lang.annotation.Annotation> AnnotationValues<A> getAnnotationValue( final Class<A> expectedType) { final EclipseNode node = getAnnotation(expectedType); return node == null ? AnnotationValues.of(expectedType, node()) : createAnnotation(expectedType, node); }
@Override public boolean handle( AnnotationValues<Synchronized> annotation, JCAnnotation ast, Node annotationNode) { Node methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != Kind.METHOD || !(methodNode.get() instanceof JCMethodDecl)) { annotationNode.addError("@Synchronized is legal only on methods."); return true; } JCMethodDecl method = (JCMethodDecl) methodNode.get(); if ((method.mods.flags & Flags.ABSTRACT) != 0) { annotationNode.addError("@Synchronized is legal only on concrete methods."); return true; } boolean isStatic = (method.mods.flags & Flags.STATIC) != 0; String lockName = annotation.getInstance().value(); boolean autoMake = false; if (lockName.length() == 0) { autoMake = true; lockName = isStatic ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME; } TreeMaker maker = methodNode.getTreeMaker(); if (fieldExists(lockName, methodNode) == MemberExistsResult.NOT_EXISTS) { if (!autoMake) { annotationNode.addError("The field " + new String(lockName) + " does not exist."); return true; } JCExpression objectType = chainDots(maker, methodNode, "java", "lang", "Object"); // We use 'new Object[0];' because quite unlike 'new Object();', empty arrays *ARE* // serializable! JCNewArray newObjectArray = maker.NewArray( chainDots(maker, methodNode, "java", "lang", "Object"), List.<JCExpression>of(maker.Literal(TypeTags.INT, 0)), null); JCVariableDecl fieldDecl = maker.VarDef( maker.Modifiers(Flags.FINAL | (isStatic ? Flags.STATIC : 0)), methodNode.toName(lockName), objectType, newObjectArray); injectField(methodNode.up(), fieldDecl); } if (method.body == null) return false; JCExpression lockNode = maker.Ident(methodNode.toName(lockName)); method.body = maker.Block(0, List.<JCStatement>of(maker.Synchronized(lockNode, method.body))); methodNode.rebuild(); return true; }
public static AnnotationValues<Accessors> getAccessorsForField(JavacNode field) { for (JavacNode node : field.down()) { if (annotationTypeMatches(Accessors.class, node)) { return createAnnotation(Accessors.class, node); } } JavacNode current = field.up(); while (current != null) { for (JavacNode node : current.down()) { if (annotationTypeMatches(Accessors.class, node)) { return createAnnotation(Accessors.class, node); } } current = current.up(); } return AnnotationValues.of(Accessors.class, field); }
public boolean handle( AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) { Data ann = annotation.getInstance(); EclipseNode typeNode = annotationNode.up(); TypeDeclaration typeDecl = null; if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; if (typeDecl == null || notAClass) { annotationNode.addError("@Data is only supported on a class."); return false; } // Careful: Generate the public static constructor (if there is one) LAST, so that any attempt // to // 'find callers' on the annotation node will find callers of the constructor, which is by far // the // most useful of the many methods built by @Data. This trick won't work for the non-static // constructor, // for whatever reason, though you can find callers of that one by focusing on the class name // itself // and hitting 'find callers'. new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); new HandleToString().generateToStringForType(typeNode, annotationNode); new HandleConstructor() .generateRequiredArgsConstructor( typeNode, AccessLevel.PUBLIC, ann.staticConstructor(), true, ast); return false; }
private void checkForBogusFieldNames(EclipseNode type, AnnotationValues<ToString> annotation) { if (annotation.isExplicit("exclude")) { for (int i : createListOfNonExistentFields( Arrays.asList(annotation.getInstance().exclude()), type, true, false)) { annotation.setWarning( "exclude", "This field does not exist, or would have been excluded anyway.", i); } } if (annotation.isExplicit("of")) { for (int i : createListOfNonExistentFields( Arrays.asList(annotation.getInstance().of()), type, false, false)) { annotation.setWarning("of", "This field does not exist.", i); } } }
@Override public void handle( AnnotationValues<Data> annotation, JCAnnotation ast, JavacNode annotationNode) { deleteAnnotationIfNeccessary(annotationNode, Data.class); JavacNode typeNode = annotationNode.up(); boolean notAClass = !isClass(typeNode); if (notAClass) { annotationNode.addError("@Data is only supported on a class."); return; } String staticConstructorName = annotation.getInstance().staticConstructor(); // TODO move this to the end OR move it to the top in eclipse. new HandleConstructor() .generateRequiredArgsConstructor( typeNode, AccessLevel.PUBLIC, staticConstructorName, true, annotationNode); new HandleGetter().generateGetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); new HandleSetter().generateSetterForType(typeNode, annotationNode, AccessLevel.PUBLIC, true); new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); new HandleToString().generateToStringForType(typeNode, annotationNode); }
public void checkForBogusFieldNames( JavacNode type, AnnotationValues<EqualsAndHashCode> annotation) { if (annotation.isExplicit("exclude")) { for (int i : createListOfNonExistentFields( List.from(annotation.getInstance().exclude()), type, true, true)) { annotation.setWarning( "exclude", "This field does not exist, or would have been excluded anyway.", i); } } if (annotation.isExplicit("of")) { for (int i : createListOfNonExistentFields( List.from(annotation.getInstance().of()), type, false, false)) { annotation.setWarning("of", "This field does not exist.", i); } } }
@Override public boolean handle( AnnotationValues<SneakyThrows> annotation, JCAnnotation ast, JavacNode annotationNode) { markAnnotationAsProcessed(annotationNode, SneakyThrows.class); Collection<String> exceptionNames = annotation.getRawExpressions("value"); if (exceptionNames.isEmpty()) { exceptionNames = Collections.singleton("java.lang.Throwable"); } java.util.List<String> exceptions = new ArrayList<String>(); for (String exception : exceptionNames) { if (exception.endsWith(".class")) exception = exception.substring(0, exception.length() - 6); exceptions.add(exception); } JavacNode owner = annotationNode.up(); switch (owner.getKind()) { case METHOD: return handleMethod(annotationNode, (JCMethodDecl) owner.get(), exceptions); default: annotationNode.addError("@SneakyThrows is legal only on methods and constructors."); return true; } }
@Override public void handle( AnnotationValues<Builder> annotation, JCAnnotation ast, JavacNode annotationNode) { handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); Builder builderInstance = annotation.getInstance(); String builderMethodName = builderInstance.builderMethodName(); String buildMethodName = builderInstance.buildMethodName(); String builderClassName = builderInstance.builderClassName(); if (builderMethodName == null) builderMethodName = "builder"; if (buildMethodName == null) buildMethodName = "build"; if (builderClassName == null) builderClassName = ""; if (!checkName("builderMethodName", builderMethodName, annotationNode)) return; if (!checkName("buildMethodName", buildMethodName, annotationNode)) return; if (!builderClassName.isEmpty()) { if (!checkName("builderClassName", builderClassName, annotationNode)) return; } deleteAnnotationIfNeccessary(annotationNode, Builder.class); deleteImportFromCompilationUnit(annotationNode, "lombok.experimental.Builder"); JavacNode parent = annotationNode.up(); java.util.List<JCExpression> typesOfParameters = new ArrayList<JCExpression>(); java.util.List<Name> namesOfParameters = new ArrayList<Name>(); JCExpression returnType; List<JCTypeParameter> typeParams = List.nil(); List<JCExpression> thrownExceptions = List.nil(); Name nameOfStaticBuilderMethod; JavacNode tdParent; JCMethodDecl fillParametersFrom = parent.get() instanceof JCMethodDecl ? ((JCMethodDecl) parent.get()) : null; if (parent.get() instanceof JCClassDecl) { tdParent = parent; JCClassDecl td = (JCClassDecl) tdParent.get(); ListBuffer<JavacNode> allFields = new ListBuffer<JavacNode>(); @SuppressWarnings("deprecation") boolean valuePresent = (hasAnnotation(lombok.Value.class, parent) || hasAnnotation(lombok.experimental.Value.class, parent)); for (JavacNode fieldNode : HandleConstructor.findAllFields(tdParent)) { JCVariableDecl fd = (JCVariableDecl) fieldNode.get(); // final fields with an initializer cannot be written to, so they can't be 'builderized'. // Unfortunately presence of @Value makes // non-final fields final, but @Value's handler hasn't done this yet, so we have to do this // math ourselves. // Value will only skip making a field final if it has an explicit @NonFinal annotation, so // we check for that. if (fd.init != null && valuePresent && !hasAnnotation(NonFinal.class, fieldNode)) continue; namesOfParameters.add(removePrefixFromField(fieldNode)); typesOfParameters.add(fd.vartype); allFields.append(fieldNode); } new HandleConstructor() .generateConstructor( tdParent, AccessLevel.PACKAGE, List.<JCAnnotation>nil(), allFields.toList(), null, SkipIfConstructorExists.I_AM_BUILDER, null, annotationNode); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = List.nil(); nameOfStaticBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null && fillParametersFrom.getName().toString().equals("<init>")) { if (!fillParametersFrom.typarams.isEmpty()) { annotationNode.addError( "@Builder is not supported on constructors with constructor type parameters."); return; } tdParent = parent.up(); JCClassDecl td = (JCClassDecl) tdParent.get(); returnType = namePlusTypeParamsToTypeReference(tdParent.getTreeMaker(), td.name, td.typarams); typeParams = td.typarams; thrownExceptions = fillParametersFrom.thrown; nameOfStaticBuilderMethod = null; if (builderClassName.isEmpty()) builderClassName = td.name.toString() + "Builder"; } else if (fillParametersFrom != null) { tdParent = parent.up(); JCClassDecl td = (JCClassDecl) tdParent.get(); if ((fillParametersFrom.mods.flags & Flags.STATIC) == 0) { annotationNode.addError( "@Builder is only supported on types, constructors, and static methods."); return; } returnType = fillParametersFrom.restype; typeParams = fillParametersFrom.typarams; thrownExceptions = fillParametersFrom.thrown; nameOfStaticBuilderMethod = fillParametersFrom.name; if (builderClassName.isEmpty()) { if (returnType instanceof JCTypeApply) { returnType = ((JCTypeApply) returnType).clazz; } if (returnType instanceof JCFieldAccess) { builderClassName = ((JCFieldAccess) returnType).name.toString() + "Builder"; } else if (returnType instanceof JCIdent) { Name n = ((JCIdent) returnType).name; for (JCTypeParameter tp : typeParams) { if (tp.name.equals(n)) { annotationNode.addError( "@Builder requires specifying 'builderClassName' if used on methods with a type parameter as return type."); return; } } builderClassName = n.toString() + "Builder"; } else if (returnType instanceof JCPrimitiveTypeTree) { builderClassName = returnType.toString() + "Builder"; if (Character.isLowerCase(builderClassName.charAt(0))) { builderClassName = Character.toTitleCase(builderClassName.charAt(0)) + builderClassName.substring(1); } } else { // This shouldn't happen. System.err.println( "Lombok bug ID#20140614-1651: javac HandleBuilder: return type to name conversion failed: " + returnType.getClass()); builderClassName = td.name.toString() + "Builder"; } } } else { annotationNode.addError( "@Builder is only supported on types, constructors, and static methods."); return; } if (fillParametersFrom != null) { for (JCVariableDecl param : fillParametersFrom.params) { namesOfParameters.add(param.name); typesOfParameters.add(param.vartype); } } JavacNode builderType = findInnerClass(tdParent, builderClassName); if (builderType == null) { builderType = makeBuilderClass(tdParent, builderClassName, typeParams, ast); } else { sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(builderType, annotationNode); } java.util.List<JavacNode> fieldNodes = addFieldsToBuilder(builderType, namesOfParameters, typesOfParameters, ast); java.util.List<JCMethodDecl> newMethods = new ArrayList<JCMethodDecl>(); for (JavacNode fieldNode : fieldNodes) { JCMethodDecl newMethod = makeSetterMethodForBuilder( builderType, fieldNode, annotationNode, builderInstance.fluent(), builderInstance.chain()); if (newMethod != null) newMethods.add(newMethod); } if (constructorExists(builderType) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl cd = HandleConstructor.createConstructor( AccessLevel.PACKAGE, List.<JCAnnotation>nil(), builderType, List.<JavacNode>nil(), null, annotationNode); if (cd != null) injectMethod(builderType, cd); } for (JCMethodDecl newMethod : newMethods) injectMethod(builderType, newMethod); if (methodExists(buildMethodName, builderType, -1) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = generateBuildMethod( buildMethodName, nameOfStaticBuilderMethod, returnType, namesOfParameters, builderType, thrownExceptions); if (md != null) injectMethod(builderType, md); } if (methodExists("toString", builderType, 0) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = HandleToString.createToString( builderType, fieldNodes, true, false, FieldAccess.ALWAYS_FIELD, ast); if (md != null) injectMethod(builderType, md); } if (methodExists(builderMethodName, tdParent, -1) == MemberExistsResult.NOT_EXISTS) { JCMethodDecl md = generateBuilderMethod(builderMethodName, builderClassName, tdParent, typeParams); if (md != null) injectMethod(tdParent, md); } }
public boolean handle( AnnotationValues<Data> annotation, Annotation ast, EclipseNode annotationNode) { Data ann = annotation.getInstance(); EclipseNode typeNode = annotationNode.up(); TypeDeclaration typeDecl = null; if (typeNode.get() instanceof TypeDeclaration) typeDecl = (TypeDeclaration) typeNode.get(); int modifiers = typeDecl == null ? 0 : typeDecl.modifiers; boolean notAClass = (modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccAnnotation | ClassFileConstants.AccEnum)) != 0; if (typeDecl == null || notAClass) { annotationNode.addError("@Data is only supported on a class."); return false; } FieldNameMangler fieldNameMangler = null; try { Class<? extends FieldNameMangler> clazz = annotation.getInstance().fieldNameMangler(); if (clazz != null) { fieldNameMangler = clazz.newInstance(); } } catch (InstantiationException ie) { // Ignore this Exception (would log if there were a logger instance) } catch (IllegalAccessException iae) { // Ignore this Exception (would log if there were a logger instance) } boolean chainable = annotation.getInstance().chainableSetters(); List<EclipseNode> nodesForConstructor = new ArrayList<EclipseNode>(); for (EclipseNode child : typeNode.down()) { if (child.getKind() != Kind.FIELD) continue; FieldDeclaration fieldDecl = (FieldDeclaration) child.get(); // Skip fields that start with $ if (fieldDecl.name.length > 0 && fieldDecl.name[0] == '$') continue; // Skip static fields. if ((fieldDecl.modifiers & ClassFileConstants.AccStatic) != 0) continue; boolean isFinal = (fieldDecl.modifiers & ClassFileConstants.AccFinal) != 0; boolean isNonNull = findAnnotations(fieldDecl, TransformationsUtil.NON_NULL_PATTERN).length != 0; if ((isFinal || isNonNull) && fieldDecl.initialization == null) nodesForConstructor.add(child); new HandleGetter().generateGetterForField(child, annotationNode.get(), fieldNameMangler); if (!isFinal) new HandleSetter() .generateSetterForField(child, annotationNode.get(), chainable, fieldNameMangler); } new HandleToString().generateToStringForType(typeNode, annotationNode); new HandleEqualsAndHashCode().generateEqualsAndHashCodeForType(typeNode, annotationNode); // Careful: Generate the public static constructor (if there is one) LAST, so that any attempt // to // 'find callers' on the annotation node will find callers of the constructor, which is by far // the // most useful of the many methods built by @Data. This trick won't work for the non-static // constructor, // for whatever reason, though you can find callers of that one by focusing on the class name // itself // and hitting 'find callers'. if (constructorExists(typeNode) == MemberExistsResult.NOT_EXISTS) { ConstructorDeclaration constructor = createConstructor( ann.staticConstructor().length() == 0, typeNode, nodesForConstructor, ast); injectMethod(typeNode, constructor); } if (ann.staticConstructor().length() > 0) { if (methodExists("of", typeNode) == MemberExistsResult.NOT_EXISTS) { MethodDeclaration staticConstructor = createStaticConstructor(ann.staticConstructor(), typeNode, nodesForConstructor, ast); injectMethod(typeNode, staticConstructor); } } return false; }
@Override public boolean handle( AnnotationValues<Synchronized> annotation, Annotation source, EclipseNode annotationNode) { int p1 = source.sourceStart - 1; int p2 = source.sourceStart - 2; long pos = (((long) p1) << 32) | p2; EclipseNode methodNode = annotationNode.up(); if (methodNode == null || methodNode.getKind() != Kind.METHOD || !(methodNode.get() instanceof MethodDeclaration)) { annotationNode.addError("@Synchronized is legal only on methods."); return true; } MethodDeclaration method = (MethodDeclaration) methodNode.get(); if (method.isAbstract()) { annotationNode.addError("@Synchronized is legal only on concrete methods."); return true; } char[] lockName = annotation.getInstance().value().toCharArray(); boolean autoMake = false; if (lockName.length == 0) { autoMake = true; lockName = method.isStatic() ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME; } if (fieldExists(new String(lockName), methodNode) == MemberExistsResult.NOT_EXISTS) { if (!autoMake) { annotationNode.addError("The field " + new String(lockName) + " does not exist."); return true; } FieldDeclaration fieldDecl = new FieldDeclaration(lockName, 0, -1); Eclipse.setGeneratedBy(fieldDecl, source); fieldDecl.declarationSourceEnd = -1; fieldDecl.modifiers = (method.isStatic() ? Modifier.STATIC : 0) | Modifier.FINAL | Modifier.PRIVATE; // We use 'new Object[0];' because quite unlike 'new Object();', empty arrays *ARE* // serializable! ArrayAllocationExpression arrayAlloc = new ArrayAllocationExpression(); Eclipse.setGeneratedBy(arrayAlloc, source); arrayAlloc.dimensions = new Expression[] {new IntLiteral(new char[] {'0'}, 0, 0)}; Eclipse.setGeneratedBy(arrayAlloc.dimensions[0], source); arrayAlloc.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, new long[] {0, 0, 0}); Eclipse.setGeneratedBy(arrayAlloc.type, source); fieldDecl.type = new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, new long[] {0, 0, 0}); Eclipse.setGeneratedBy(fieldDecl.type, source); fieldDecl.initialization = arrayAlloc; injectFieldSuppressWarnings(annotationNode.up().up(), fieldDecl); } if (method.statements == null) return false; Block block = new Block(0); Eclipse.setGeneratedBy(block, source); block.statements = method.statements; Expression lockVariable; if (method.isStatic()) lockVariable = new QualifiedNameReference( new char[][] {methodNode.up().getName().toCharArray(), lockName}, new long[] {pos, pos}, p1, p2); else { lockVariable = new FieldReference(lockName, pos); ThisReference thisReference = new ThisReference(p1, p2); Eclipse.setGeneratedBy(thisReference, source); ((FieldReference) lockVariable).receiver = thisReference; } Eclipse.setGeneratedBy(lockVariable, source); method.statements = new Statement[] {new SynchronizedStatement(lockVariable, block, 0, 0)}; Eclipse.setGeneratedBy(method.statements[0], source); methodNode.rebuild(); return true; }