/** * Copyright (c) 2013 itemis AG (http://www.itemis.eu) and others. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * SPDX-License-Identifier: EPL-2.0 */ package org.eclipse.xtend.lib.annotations; import com.google.common.annotations.Beta; import com.google.common.base.Objects; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; import org.eclipse.xtend.lib.annotations.AccessorType; import org.eclipse.xtend.lib.annotations.Accessors; import org.eclipse.xtend.lib.annotations.AccessorsDeprecationPolicy; import org.eclipse.xtend.lib.annotations.Data; import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor; import org.eclipse.xtend.lib.annotations.FinalFieldsConstructorProcessor; import org.eclipse.xtend.lib.macro.TransformationContext; import org.eclipse.xtend.lib.macro.TransformationParticipant; import org.eclipse.xtend.lib.macro.declaration.AnnotationReference; import org.eclipse.xtend.lib.macro.declaration.AnnotationTarget; import org.eclipse.xtend.lib.macro.declaration.EnumerationValueDeclaration; import org.eclipse.xtend.lib.macro.declaration.FieldDeclaration; import org.eclipse.xtend.lib.macro.declaration.MethodDeclaration; import org.eclipse.xtend.lib.macro.declaration.MutableClassDeclaration; import org.eclipse.xtend.lib.macro.declaration.MutableFieldDeclaration; import org.eclipse.xtend.lib.macro.declaration.MutableMemberDeclaration; import org.eclipse.xtend.lib.macro.declaration.MutableMethodDeclaration; import org.eclipse.xtend.lib.macro.declaration.MutableParameterDeclaration; import org.eclipse.xtend.lib.macro.declaration.TypeReference; import org.eclipse.xtend.lib.macro.declaration.Visibility; import org.eclipse.xtend2.lib.StringConcatenation; import org.eclipse.xtend2.lib.StringConcatenationClient; import org.eclipse.xtext.xbase.lib.CollectionLiterals; import org.eclipse.xtext.xbase.lib.Conversions; import org.eclipse.xtext.xbase.lib.Extension; import org.eclipse.xtext.xbase.lib.Functions.Function1; import org.eclipse.xtext.xbase.lib.IterableExtensions; import org.eclipse.xtext.xbase.lib.ListExtensions; import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; import org.eclipse.xtext.xbase.lib.Pure; import org.eclipse.xtext.xbase.lib.StringExtensions; /** * @since 2.7 * @noextend * @noreference */ @Beta @SuppressWarnings("all") public class AccessorsProcessor implements TransformationParticipant<MutableMemberDeclaration> { /** * @since 2.7 * @noextend * @noreference */ @Beta public static class Util { @Extension private TransformationContext context; public Util(final TransformationContext context) { this.context = context; } public Visibility toVisibility(final AccessorType type) { Visibility _switchResult = null; if (type != null) { switch (type) { case PUBLIC_GETTER: _switchResult = Visibility.PUBLIC; break; case PROTECTED_GETTER: _switchResult = Visibility.PROTECTED; break; case PACKAGE_GETTER: _switchResult = Visibility.DEFAULT; break; case PRIVATE_GETTER: _switchResult = Visibility.PRIVATE; break; case PUBLIC_SETTER: _switchResult = Visibility.PUBLIC; break; case PROTECTED_SETTER: _switchResult = Visibility.PROTECTED; break; case PACKAGE_SETTER: _switchResult = Visibility.DEFAULT; break; case PRIVATE_SETTER: _switchResult = Visibility.PRIVATE; break; default: StringConcatenation _builder = new StringConcatenation(); _builder.append("Cannot convert "); _builder.append(type); throw new IllegalArgumentException(_builder.toString()); } } else { StringConcatenation _builder = new StringConcatenation(); _builder.append("Cannot convert "); _builder.append(type); throw new IllegalArgumentException(_builder.toString()); } return _switchResult; } public boolean hasGetter(final FieldDeclaration it) { final Function1<String, Boolean> _function = (String name) -> { MethodDeclaration _findDeclaredMethod = it.getDeclaringType().findDeclaredMethod(name); return Boolean.valueOf((_findDeclaredMethod != null)); }; return IterableExtensions.<String>exists(this.getPossibleGetterNames(it), _function); } public boolean shouldAddGetter(final FieldDeclaration it) { return ((!this.hasGetter(it)) && (this.getGetterType(it) != AccessorType.NONE)); } @SuppressWarnings("unchecked") public AccessorType getGetterType(final FieldDeclaration it) { AnnotationReference _elvis = null; AnnotationReference _accessorsAnnotation = this.getAccessorsAnnotation(it); if (_accessorsAnnotation != null) { _elvis = _accessorsAnnotation; } else { AnnotationReference _accessorsAnnotation_1 = this.getAccessorsAnnotation(it.getDeclaringType()); _elvis = _accessorsAnnotation_1; } final AnnotationReference annotation = _elvis; if ((annotation != null)) { final Function1<EnumerationValueDeclaration, AccessorType> _function = (EnumerationValueDeclaration it_1) -> { return AccessorType.valueOf(it_1.getSimpleName()); }; final List<AccessorType> types = ListExtensions.<EnumerationValueDeclaration, AccessorType>map(((List<EnumerationValueDeclaration>)Conversions.doWrapArray(annotation.getEnumArrayValue("value"))), _function); AccessorType _elvis_1 = null; final Function1<AccessorType, Boolean> _function_1 = (AccessorType it_1) -> { return Boolean.valueOf(it_1.name().endsWith("GETTER")); }; AccessorType _findFirst = IterableExtensions.<AccessorType>findFirst(types, _function_1); if (_findFirst != null) { _elvis_1 = _findFirst; } else { _elvis_1 = AccessorType.NONE; } return _elvis_1; } return null; } public AnnotationReference getAccessorsAnnotation(final AnnotationTarget it) { return it.findAnnotation(this.context.findTypeGlobally(Accessors.class)); } public AnnotationReference getDeprecatedAnnotation(final AnnotationTarget it) { return it.findAnnotation(this.context.findTypeGlobally(Deprecated.class)); } public AccessorsDeprecationPolicy getDeprecationPolicyAsEnum(final AnnotationReference annot) { return AccessorsDeprecationPolicy.valueOf(annot.getEnumValue("deprecationPolicy").getSimpleName()); } public Object validateGetter(final MutableFieldDeclaration field) { return null; } public String getGetterName(final FieldDeclaration it) { return IterableExtensions.<String>head(this.getPossibleGetterNames(it)); } public List<String> getPossibleGetterNames(final FieldDeclaration it) { final ArrayList<String> names = CollectionLiterals.<String>newArrayList(); if ((((this.isBooleanType(this.orObject(it.getType())) && it.getSimpleName().startsWith("is")) && (it.getSimpleName().length() > 2)) && Character.isUpperCase(it.getSimpleName().charAt(2)))) { String _simpleName = it.getSimpleName(); names.add(_simpleName); } List<String> _xifexpression = null; boolean _isBooleanType = this.isBooleanType(this.orObject(it.getType())); if (_isBooleanType) { _xifexpression = Collections.<String>unmodifiableList(CollectionLiterals.<String>newArrayList("is", "get")); } else { _xifexpression = Collections.<String>unmodifiableList(CollectionLiterals.<String>newArrayList("get")); } final Function1<String, String> _function = (String prefix) -> { String _firstUpper = StringExtensions.toFirstUpper(it.getSimpleName()); return (prefix + _firstUpper); }; names.addAll(ListExtensions.<String, String>map(_xifexpression, _function)); return names; } public boolean isBooleanType(final TypeReference it) { return ((!it.isInferred()) && Objects.equal(it, this.context.getPrimitiveBoolean())); } public void addGetter(final MutableFieldDeclaration field, final Visibility visibility) { this.validateGetter(field); field.markAsRead(); final Procedure1<MutableMethodDeclaration> _function = (MutableMethodDeclaration it) -> { this.context.setPrimarySourceElement(it, this.context.getPrimarySourceElement(field)); it.addAnnotation(this.context.newAnnotationReference(Pure.class)); final Iterable<? extends MethodDeclaration> superGetters = it.getOverriddenOrImplementedMethods(); boolean _isEmpty = IterableExtensions.isEmpty(superGetters); boolean _not = (!_isEmpty); if (_not) { final Function1<MethodDeclaration, Boolean> _function_1 = (MethodDeclaration it_1) -> { return Boolean.valueOf(it_1.isFinal()); }; boolean _exists = IterableExtensions.exists(superGetters, _function_1); if (_exists) { StringConcatenation _builder = new StringConcatenation(); _builder.append("Adding a getter to the field "); String _simpleName = field.getSimpleName(); _builder.append(_simpleName); _builder.append(" would override a final method."); this.context.addError(field, _builder.toString()); } else { it.addAnnotation(this.context.newAnnotationReference(Override.class)); } } final AnnotationReference annot = this.getAccessorsAnnotation(field); if ((annot != null)) { AccessorsDeprecationPolicy _deprecationPolicyAsEnum = this.getDeprecationPolicyAsEnum(annot); if (_deprecationPolicyAsEnum != null) { switch (_deprecationPolicyAsEnum) { case ALWAYS: case ONLY_GETTER: it.addAnnotation(this.context.newAnnotationReference(Deprecated.class)); break; case SAME_AS_FIELD: AnnotationReference _deprecatedAnnotation = this.getDeprecatedAnnotation(field); boolean _tripleNotEquals = (_deprecatedAnnotation != null); if (_tripleNotEquals) { it.addAnnotation(this.context.newAnnotationReference(Deprecated.class)); } break; case ONLY_SETTER: case NEVER: break; default: StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Cannot determine deprecation policy for field "); String _simpleName_1 = field.getSimpleName(); _builder_1.append(_simpleName_1); throw new IllegalArgumentException(_builder_1.toString()); } } else { StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Cannot determine deprecation policy for field "); String _simpleName_1 = field.getSimpleName(); _builder_1.append(_simpleName_1); throw new IllegalArgumentException(_builder_1.toString()); } } it.setReturnType(this.orObject(field.getType())); StringConcatenationClient _client = new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) { _builder.append("return "); Object _fieldOwner = Util.this.fieldOwner(field); _builder.append(_fieldOwner); _builder.append("."); String _simpleName = field.getSimpleName(); _builder.append(_simpleName); _builder.append(";"); } }; it.setBody(_client); it.setStatic(field.isStatic()); it.setVisibility(visibility); }; field.getDeclaringType().addMethod(this.getGetterName(field), _function); } @SuppressWarnings("unchecked") public AccessorType getSetterType(final FieldDeclaration it) { AnnotationReference _elvis = null; AnnotationReference _accessorsAnnotation = this.getAccessorsAnnotation(it); if (_accessorsAnnotation != null) { _elvis = _accessorsAnnotation; } else { AnnotationReference _accessorsAnnotation_1 = this.getAccessorsAnnotation(it.getDeclaringType()); _elvis = _accessorsAnnotation_1; } final AnnotationReference annotation = _elvis; if ((annotation != null)) { final Function1<EnumerationValueDeclaration, AccessorType> _function = (EnumerationValueDeclaration it_1) -> { return AccessorType.valueOf(it_1.getSimpleName()); }; final List<AccessorType> types = ListExtensions.<EnumerationValueDeclaration, AccessorType>map(((List<EnumerationValueDeclaration>)Conversions.doWrapArray(annotation.getEnumArrayValue("value"))), _function); AccessorType _elvis_1 = null; final Function1<AccessorType, Boolean> _function_1 = (AccessorType it_1) -> { return Boolean.valueOf(it_1.name().endsWith("SETTER")); }; AccessorType _findFirst = IterableExtensions.<AccessorType>findFirst(types, _function_1); if (_findFirst != null) { _elvis_1 = _findFirst; } else { _elvis_1 = AccessorType.NONE; } return _elvis_1; } return null; } private Object fieldOwner(final MutableFieldDeclaration it) { Object _xifexpression = null; boolean _isStatic = it.isStatic(); if (_isStatic) { _xifexpression = this.context.newTypeReference(it.getDeclaringType()); } else { _xifexpression = "this"; } return _xifexpression; } public boolean hasSetter(final FieldDeclaration it) { MethodDeclaration _findDeclaredMethod = it.getDeclaringType().findDeclaredMethod(this.getSetterName(it), this.orObject(it.getType())); return (_findDeclaredMethod != null); } public String getSetterName(final FieldDeclaration it) { String _firstUpper = StringExtensions.toFirstUpper(it.getSimpleName()); return ("set" + _firstUpper); } public boolean shouldAddSetter(final FieldDeclaration it) { return (((!it.isFinal()) && (!this.hasSetter(it))) && (this.getSetterType(it) != AccessorType.NONE)); } public void validateSetter(final MutableFieldDeclaration field) { boolean _isFinal = field.isFinal(); if (_isFinal) { this.context.addError(field, "Cannot set a final field"); } if (((field.getType() == null) || field.getType().isInferred())) { this.context.addError(field, "Type cannot be inferred."); return; } } public void addSetter(final MutableFieldDeclaration field, final Visibility visibility) { this.validateSetter(field); final Procedure1<MutableMethodDeclaration> _function = (MutableMethodDeclaration it) -> { this.context.setPrimarySourceElement(it, this.context.getPrimarySourceElement(field)); it.setReturnType(this.context.getPrimitiveVoid()); final Iterable<? extends MethodDeclaration> superSetters = it.getOverriddenOrImplementedMethods(); boolean _isEmpty = IterableExtensions.isEmpty(superSetters); boolean _not = (!_isEmpty); if (_not) { final Function1<MethodDeclaration, Boolean> _function_1 = (MethodDeclaration it_1) -> { return Boolean.valueOf(it_1.isFinal()); }; boolean _exists = IterableExtensions.exists(superSetters, _function_1); if (_exists) { StringConcatenation _builder = new StringConcatenation(); _builder.append("Adding a setter to the field "); String _simpleName = field.getSimpleName(); _builder.append(_simpleName); _builder.append(" would override a final method."); this.context.addError(field, _builder.toString()); } else { it.addAnnotation(this.context.newAnnotationReference(Override.class)); } } final AnnotationReference annot = this.getAccessorsAnnotation(field); if ((annot != null)) { AccessorsDeprecationPolicy _deprecationPolicyAsEnum = this.getDeprecationPolicyAsEnum(annot); if (_deprecationPolicyAsEnum != null) { switch (_deprecationPolicyAsEnum) { case ALWAYS: case ONLY_SETTER: it.addAnnotation(this.context.newAnnotationReference(Deprecated.class)); break; case SAME_AS_FIELD: AnnotationReference _deprecatedAnnotation = this.getDeprecatedAnnotation(field); boolean _tripleNotEquals = (_deprecatedAnnotation != null); if (_tripleNotEquals) { it.addAnnotation(this.context.newAnnotationReference(Deprecated.class)); } break; case ONLY_GETTER: case NEVER: break; default: StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Cannot determine deprecation policy for field "); String _simpleName_1 = field.getSimpleName(); _builder_1.append(_simpleName_1); throw new IllegalArgumentException(_builder_1.toString()); } } else { StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Cannot determine deprecation policy for field "); String _simpleName_1 = field.getSimpleName(); _builder_1.append(_simpleName_1); throw new IllegalArgumentException(_builder_1.toString()); } } final MutableParameterDeclaration param = it.addParameter(field.getSimpleName(), this.orObject(field.getType())); StringConcatenationClient _client = new StringConcatenationClient() { @Override protected void appendTo(StringConcatenationClient.TargetStringConcatenation _builder) { Object _fieldOwner = Util.this.fieldOwner(field); _builder.append(_fieldOwner); _builder.append("."); String _simpleName = field.getSimpleName(); _builder.append(_simpleName); _builder.append(" = "); String _simpleName_1 = param.getSimpleName(); _builder.append(_simpleName_1); _builder.append(";"); } }; it.setBody(_client); it.setStatic(field.isStatic()); it.setVisibility(visibility); }; field.getDeclaringType().addMethod(this.getSetterName(field), _function); } private TypeReference orObject(final TypeReference ref) { TypeReference _xifexpression = null; if ((ref == null)) { _xifexpression = this.context.getObject(); } else { _xifexpression = ref; } return _xifexpression; } } @Override public void doTransform(final List<? extends MutableMemberDeclaration> elements, @Extension final TransformationContext context) { final Consumer<MutableMemberDeclaration> _function = (MutableMemberDeclaration it) -> { this.transform(it, context); }; elements.forEach(_function); } protected void _transform(final MutableFieldDeclaration it, @Extension final TransformationContext context) { @Extension final AccessorsProcessor.Util util = new AccessorsProcessor.Util(context); final AnnotationReference annot = util.getAccessorsAnnotation(it); boolean _shouldAddGetter = util.shouldAddGetter(it); if (_shouldAddGetter) { util.addGetter(it, util.toVisibility(util.getGetterType(it))); } else { if (((annot != null) && (util.getDeprecationPolicyAsEnum(annot) == AccessorsDeprecationPolicy.ONLY_GETTER))) { StringConcatenation _builder = new StringConcatenation(); _builder.append("Field "); String _simpleName = it.getSimpleName(); _builder.append(_simpleName); _builder.append(" needs no getter, but deprecationPolicy is ONLY_GETTER."); _builder.newLineIfNotEmpty(); _builder.append("Explicitly setting it has no effect, as no getter will be generated."); _builder.newLine(); _builder.append("Use deprecation policy NEVER to disable accessors deprecation and remove this warning."); _builder.newLine(); context.addWarning(it, _builder.toString()); } } boolean _shouldAddSetter = util.shouldAddSetter(it); if (_shouldAddSetter) { util.addSetter(it, util.toVisibility(util.getSetterType(it))); } else { if (((annot != null) && (util.getDeprecationPolicyAsEnum(annot) == AccessorsDeprecationPolicy.ONLY_SETTER))) { StringConcatenation _builder_1 = new StringConcatenation(); _builder_1.append("Field "); String _simpleName_1 = it.getSimpleName(); _builder_1.append(_simpleName_1); _builder_1.append(" needs no setter, but deprecationPolicy is ONLY_SETTER."); _builder_1.newLineIfNotEmpty(); _builder_1.append("Explicitly setting it has no effect, as no setter will be generated."); _builder_1.newLine(); _builder_1.append("Use deprecation policy NEVER to disable accessors deprecation and remove this warning."); _builder_1.newLine(); context.addWarning(it, _builder_1.toString()); } } } protected void _transform(final MutableClassDeclaration it, @Extension final TransformationContext context) { AnnotationReference _findAnnotation = it.findAnnotation(context.findTypeGlobally(Data.class)); boolean _tripleNotEquals = (_findAnnotation != null); if (_tripleNotEquals) { return; } @Extension final FinalFieldsConstructorProcessor.Util requiredArgsUtil = new FinalFieldsConstructorProcessor.Util(context); if ((requiredArgsUtil.needsFinalFieldConstructor(it) || (it.findAnnotation(context.findTypeGlobally(FinalFieldsConstructor.class)) != null))) { requiredArgsUtil.addFinalFieldsConstructor(it); } final Function1<MutableFieldDeclaration, Boolean> _function = (MutableFieldDeclaration it_1) -> { return Boolean.valueOf(((!it_1.isStatic()) && context.isThePrimaryGeneratedJavaElement(it_1))); }; final Consumer<MutableFieldDeclaration> _function_1 = (MutableFieldDeclaration it_1) -> { this._transform(it_1, context); }; IterableExtensions.filter(it.getDeclaredFields(), _function).forEach(_function_1); } public void transform(final MutableMemberDeclaration it, final TransformationContext context) { if (it instanceof MutableClassDeclaration) { _transform((MutableClassDeclaration)it, context); return; } else if (it instanceof MutableFieldDeclaration) { _transform((MutableFieldDeclaration)it, context); return; } else { throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object>asList(it, context).toString()); } } }