blob: 62ba3e6161881e5530e0f69cdd9d85c5e04cda4d [file] [log] [blame]
/**
* 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.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());
}
}
}