edelta.refactorings.lib.EdeltaBadSmellsFinder Maven / Gradle / Ivy
The newest version!
package edelta.refactorings.lib;
import com.google.common.collect.Iterables;
import edelta.lib.EdeltaDefaultRuntime;
import edelta.lib.EdeltaRuntime;
import edelta.lib.EdeltaUtils;
import edelta.refactorings.lib.helper.EdeltaFeatureEqualityHelper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.Functions.Function2;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ListExtensions;
import org.eclipse.xtext.xbase.lib.MapExtensions;
import org.eclipse.xtext.xbase.lib.Pair;
@SuppressWarnings("all")
public class EdeltaBadSmellsFinder extends EdeltaDefaultRuntime {
public EdeltaBadSmellsFinder(final EdeltaRuntime other) {
super(other);
}
/**
* Finds all the features that are structurally equal
* in the given {@link EPackage}.
*
* Note that this takes into consideration the name and type,
* but also other properties like lowerBound, unique, etc.
*
* For example, given these EClasses
*
*
* C1 {
* A1 : EString
* }
*
* C2 {
* A1 : EString
* }
*
*
* It returns the map with this entry
*
*
* (A1 : EString) -> [ C1:A1, C2:A1 ]
*
*
* @param ePackage
*/
public Map> findDuplicatedFeatures(final EPackage ePackage) {
final BiPredicate _function = (EStructuralFeature existing, EStructuralFeature current) -> {
return new EdeltaFeatureEqualityHelper().equals(existing, current);
};
return this.findDuplicatedFeaturesCustom(ePackage, _function);
}
/**
* Allows you to specify the lambda checking for equality of features.
*
* @param ePackage
* @param matcher
*/
public Map> findDuplicatedFeaturesCustom(final EPackage ePackage, final BiPredicate matcher) {
final List allFeatures = EdeltaUtils.allEStructuralFeatures(ePackage);
final Map> result = this.findDuplicatedFeaturesInCollection(allFeatures, matcher);
final Consumer>> _function = (Map.Entry> it) -> {
final Supplier _function_1 = () -> {
final Function1 _function_2 = (EStructuralFeature it_1) -> {
return EdeltaUtils.getEObjectRepr(it_1);
};
String _join = IterableExtensions.join(ListExtensions.map(it.getValue(), _function_2), ", ");
return ("Duplicate features: " + _join);
};
this.logInfo(_function_1);
};
result.entrySet().forEach(_function);
return result;
}
/**
* Finds all the features that are structurally equal
* in the given collection.
*
* Note that this takes into consideration the name and type,
* but also other properties like lowerBound, unique, etc.
*
* For example, given these EClasses
*
*
* C1 {
* A1 : EString
* }
*
* C2 {
* A1 : EString
* }
*
*
* And the collection of features [ C1:A1, C2:A1 ]
* it returns the map with this entry
*
*
* (A1 : EString) -> [ C1:A1, C2:A1 ]
*
*
* It allows you to specify the lambda checking for equality of features.
*
* @param features
* @param matcher
*/
public Map> findDuplicatedFeaturesInCollection(final Collection features, final BiPredicate matcher) {
final LinkedHashMap> map = CollectionLiterals.>newLinkedHashMap();
for (final EStructuralFeature f : features) {
{
final Function1>, Boolean> _function = (Map.Entry> it) -> {
return Boolean.valueOf(matcher.test(it.getKey(), f));
};
final Map.Entry> existing = IterableExtensions.>>findFirst(map.entrySet(), _function);
if ((existing != null)) {
List _value = existing.getValue();
_value.add(f);
} else {
map.put(f, CollectionLiterals.newArrayList(f));
}
}
}
final Function2, Boolean> _function = (EStructuralFeature key, List values) -> {
int _size = values.size();
return Boolean.valueOf((_size > 1));
};
return MapExtensions.>filter(map, _function);
}
/**
* If a class has more than one direct subclass, it finds duplicate features in all
* of its direct subclasses.
*
* Returns a map where the key is the class with more than one direct subclass
* with duplicate features; the value is another map as returned by
* {@link #findDuplicatedFeaturesInCollection(Collection, BiPredicate)}
*/
public LinkedHashMap>> findDuplicatedFeaturesInSubclasses(final EPackage ePackage) {
final LinkedHashMap>> map = CollectionLiterals.>>newLinkedHashMap();
List _allEClasses = EdeltaUtils.allEClasses(ePackage);
for (final EClass c : _allEClasses) {
{
final Iterable directSubclasses = this.directSubclasses(c);
final int numOfSubclasses = IterableExtensions.size(directSubclasses);
if ((numOfSubclasses > 1)) {
final Function1> _function = (EClass it) -> {
return it.getEStructuralFeatures();
};
final BiPredicate _function_1 = (EStructuralFeature existing, EStructuralFeature current) -> {
return new EdeltaFeatureEqualityHelper().equals(existing, current);
};
final Map> candidates = this.findDuplicatedFeaturesInCollection(
IterableExtensions.toList(Iterables.concat(IterableExtensions.>map(directSubclasses, _function))), _function_1);
final Function2, Boolean> _function_2 = (EStructuralFeature key, List values) -> {
int _size = values.size();
return Boolean.valueOf((_size == numOfSubclasses));
};
final Map> duplicates = MapExtensions.>filter(candidates, _function_2);
boolean _isEmpty = duplicates.isEmpty();
boolean _not = (!_isEmpty);
if (_not) {
map.put(c, duplicates);
final Consumer>> _function_3 = (Map.Entry> it) -> {
final Supplier _function_4 = () -> {
String _eObjectRepr = EdeltaUtils.getEObjectRepr(c);
String _plus = ("In subclasses of " + _eObjectRepr);
String _plus_1 = (_plus + ", duplicate features: ");
final Function1 _function_5 = (EStructuralFeature it_1) -> {
return EdeltaUtils.getEObjectRepr(it_1);
};
String _join = IterableExtensions.join(ListExtensions.map(it.getValue(), _function_5), ", ");
return (_plus_1 + _join);
};
this.logInfo(_function_4);
};
duplicates.entrySet().forEach(_function_3);
}
}
}
}
return map;
}
/**
* Finds all the features corresponding to a redundant container,
* that is, a missed opposite reference to the container.
*
* The result consists of an iterable of pairs where the key
* is the reference corresponding to the redundant container
* and the value is the reference that should correspond to the
* opposite reference.
*
* For example, if "Bank" has a containment feature "clients",
* and the "Client" has a non-containment feature "bank", which
* is not set as the opposite of "clients", then the detected
* redundant container will be the pair "Client:bank" -> "Bank:clients".
*
* This form should make the corresponding refactoring trivial to
* implement, since all the information are in the pair.
*/
public Iterable> findRedundantContainers(final EPackage ePackage) {
final Function1>> _function = (EClass it) -> {
return this.findRedundantContainers(it);
};
return Iterables.>concat(ListExtensions.>>map(EdeltaUtils.allEClasses(ePackage), _function));
}
/**
* see {@link #findRedundantContainers(EPackage)}
*/
public ArrayList> findRedundantContainers(final EClass cl) {
final ArrayList> redundantContainers = CollectionLiterals.>newArrayList();
final Function1 _function = (EReference it) -> {
return Boolean.valueOf(it.isContainment());
};
final Iterable containmentReferences = IterableExtensions.filter(cl.getEReferences(), _function);
for (final EReference containmentReference : containmentReferences) {
{
final Function1 _function_1 = (EReference it) -> {
return Boolean.valueOf(((((!it.isContainment()) &&
it.isRequired()) &&
(it.getEOpposite() == null)) &&
(it.getEReferenceType() == cl)));
};
final EReference redundant = IterableExtensions.head(IterableExtensions.filter(containmentReference.getEReferenceType().getEReferences(), _function_1));
if ((redundant != null)) {
Pair _mappedTo = Pair.of(redundant, containmentReference);
redundantContainers.add(_mappedTo);
final Supplier _function_2 = () -> {
String _eObjectRepr = EdeltaUtils.getEObjectRepr(containmentReference);
String _plus = ("Redundant container: " + _eObjectRepr);
String _plus_1 = (_plus + " -> ");
String _eObjectRepr_1 = EdeltaUtils.getEObjectRepr(redundant);
return (_plus_1 + _eObjectRepr_1);
};
this.logInfo(_function_2);
}
}
}
return redundantContainers;
}
/**
* see {@link #isDeadClassifier(EClassifier)}
*/
public List findDeadClassifiers(final EPackage ePackage) {
final Function1 _function = (EClassifier it) -> {
return Boolean.valueOf(this.isDeadClassifier(it));
};
return IterableExtensions.toList(IterableExtensions.filter(ePackage.getEClassifiers(), _function));
}
/**
* Whether {@link #doesNotReferToClasses(EClassifier)} and
* {@link #isNotReferredByClassifiers(EClassifier)}
*/
public boolean isDeadClassifier(final EClassifier cl) {
final boolean result = (this.doesNotReferToClasses(cl) && this.isNotReferredByClassifiers(cl));
if (result) {
final Supplier _function = () -> {
String _eObjectRepr = EdeltaUtils.getEObjectRepr(cl);
return ("Dead classifier: " + _eObjectRepr);
};
this.logInfo(_function);
}
return result;
}
/**
* Whether the passed EClassifier does not refer to any EClass
*/
public boolean doesNotReferToClasses(final EClassifier c) {
return IterableExtensions.isEmpty(Iterables.filter(EcoreUtil.CrossReferencer.find(CollectionLiterals.newArrayList(c)).keySet(), EClass.class));
}
/**
* Whether the passed EClassifier is not referred by EClassifiers.
*/
public boolean isNotReferredByClassifiers(final EClassifier cl) {
return EcoreUtil.UsageCrossReferencer.find(cl, EdeltaUtils.packagesToInspect(cl)).isEmpty();
}
/**
* Returns a map where the key is an EClass (superclass)
* and the associated value is a list of subclasses that are
* considered matching the "classification by hierarchy" bad smell.
*/
public Map> findClassificationByHierarchy(final EPackage ePackage) {
final Function1 _function = (EClass it) -> {
return Boolean.valueOf((((it.getESuperTypes().size() == 1) &&
it.getEStructuralFeatures().isEmpty()) &&
this.isNotReferredByClassifiers(it)));
};
final Function1 _function_1 = (EClass it) -> {
return IterableExtensions.head(it.getESuperTypes());
};
final Function2, Boolean> _function_2 = (EClass base, List subclasses) -> {
int _size = subclasses.size();
return Boolean.valueOf((_size > 1));
};
final Map> classification = MapExtensions.>filter(IterableExtensions.groupBy(IterableExtensions.filter(EdeltaUtils.allEClasses(ePackage), _function), _function_1), _function_2);
final Consumer>> _function_3 = (Map.Entry> it) -> {
final Supplier _function_4 = () -> {
String _eObjectRepr = EdeltaUtils.getEObjectRepr(it.getKey());
String _plus = ("Classification by hierarchy: " + _eObjectRepr);
String _plus_1 = (_plus + " - ");
String _plus_2 = (_plus_1 + "subclasses[");
final Function1 _function_5 = (EClass it_1) -> {
return EdeltaUtils.getEObjectRepr(it_1);
};
String _join = IterableExtensions.join(ListExtensions.map(it.getValue(), _function_5), ",");
String _plus_3 = (_plus_2 + _join);
return (_plus_3 + "]");
};
this.logInfo(_function_4);
};
classification.entrySet().forEach(_function_3);
return classification;
}
/**
* Finds base classes that should be set as abstract,
* since they have subclasses.
*/
public Iterable findConcreteAbstractMetaclasses(final EPackage ePackage) {
final Function1 _function = (EClass cl) -> {
return Boolean.valueOf(((!cl.isAbstract()) &&
this.hasSubclasses(cl)));
};
final Iterable classes = IterableExtensions.filter(EdeltaUtils.allEClasses(ePackage), _function);
final Consumer _function_1 = (EClass it) -> {
final Supplier _function_2 = () -> {
String _eObjectRepr = EdeltaUtils.getEObjectRepr(it);
return ("Concrete abstract class: " + _eObjectRepr);
};
this.logInfo(_function_2);
};
classes.forEach(_function_1);
return classes;
}
public boolean hasSubclasses(final EClass cl) {
boolean _isEmpty = IterableExtensions.isEmpty(this.directSubclasses(cl));
return (!_isEmpty);
}
public Iterable directSubclasses(final EClass cl) {
final Function1 _function = (EStructuralFeature.Setting it) -> {
EStructuralFeature _eStructuralFeature = it.getEStructuralFeature();
return Boolean.valueOf((_eStructuralFeature == getEReference("ecore", "EClass", "eSuperTypes")));
};
final Function1 _function_1 = (EStructuralFeature.Setting it) -> {
EObject _eObject = it.getEObject();
return ((EClass) _eObject);
};
return IterableExtensions.map(IterableExtensions.filter(EcoreUtil.UsageCrossReferencer.find(cl, EdeltaUtils.packagesToInspect(cl)), _function), _function_1);
}
/**
* Finds abstract classes that should be concrete,
* since they have no subclasses.
*/
public Iterable findAbstractConcreteMetaclasses(final EPackage ePackage) {
final Function1 _function = (EClass cl) -> {
return Boolean.valueOf((cl.isAbstract() &&
(!this.hasSubclasses(cl))));
};
final Iterable classes = IterableExtensions.filter(EdeltaUtils.allEClasses(ePackage), _function);
final Consumer _function_1 = (EClass it) -> {
final Supplier _function_2 = () -> {
String _eObjectRepr = EdeltaUtils.getEObjectRepr(it);
return ("Abstract concrete class: " + _eObjectRepr);
};
this.logInfo(_function_2);
};
classes.forEach(_function_1);
return classes;
}
/**
* Finds classes that are abstract though they have only concrete superclasses.
*/
public Iterable findAbstractSubclassesOfConcreteSuperclasses(final EPackage ePackage) {
final Function1 _function = (EClass cl) -> {
return Boolean.valueOf(((cl.isAbstract() &&
(!cl.getESuperTypes().isEmpty())) &&
IterableExtensions.forall(cl.getESuperTypes(), ((Function1) (EClass it) -> {
boolean _isAbstract = it.isAbstract();
return Boolean.valueOf((!_isAbstract));
}))));
};
final Iterable classes = IterableExtensions.filter(EdeltaUtils.allEClasses(ePackage), _function);
final Consumer _function_1 = (EClass it) -> {
final Supplier _function_2 = () -> {
String _eObjectRepr = EdeltaUtils.getEObjectRepr(it);
return ("Abstract class with concrete superclasses: " + _eObjectRepr);
};
this.logInfo(_function_2);
};
classes.forEach(_function_1);
return classes;
}
@Override
public void performSanityChecks() throws Exception {
ensureEPackageIsLoaded("ecore");
}
}