All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edelta.refactorings.lib.EdeltaBadSmellsFinder.edelta Maven / Gradle / Ivy

import edelta.refactorings.lib.helper.EdeltaFeatureEqualityHelper
import java.util.Collection
import java.util.List
import java.util.Map
import java.util.function.BiPredicate
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EClassifier
import org.eclipse.emf.ecore.EPackage
import org.eclipse.emf.ecore.EStructuralFeature
import org.eclipse.emf.ecore.util.EcoreUtil
import org.eclipse.emf.ecore.util.EcoreUtil.CrossReferencer
import org.eclipse.emf.ecore.util.EcoreUtil.UsageCrossReferencer

package edelta.refactorings.lib

metamodel "ecore"

/**
 * 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 */ def findDuplicatedFeatures(EPackage ePackage) { return findDuplicatedFeaturesCustom( ePackage, [existing, current| new EdeltaFeatureEqualityHelper().equals(existing, current) ] ) } /** * Allows you to specify the lambda checking for equality of features. * * @param ePackage * @param matcher */ def findDuplicatedFeaturesCustom(EPackage ePackage, BiPredicate matcher) { val allFeatures = ePackage.allEStructuralFeatures val result = findDuplicatedFeaturesInCollection(allFeatures, matcher) result.entrySet.forEach[ logInfo[ "Duplicate features: " + value.map[getEObjectRepr(it)].join(", ") ] ] 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 */ def findDuplicatedFeaturesInCollection(Collection features, BiPredicate matcher) { val map = >newLinkedHashMap for (f : features) { val existing = map.entrySet.findFirst[matcher.test(it.key, f)] if (existing !== null) { existing.value += f } else { map.put(f, newArrayList(f)) } } // only entries with a mapped list of size > 1 are duplicates return map.filter[key, values| values.size > 1] } /** * 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)} */ def findDuplicatedFeaturesInSubclasses(EPackage ePackage) { val map = newLinkedHashMap for (c : ePackage.allEClasses) { val directSubclasses = c.directSubclasses val numOfSubclasses = directSubclasses.size if (numOfSubclasses > 1) { val candidates = findDuplicatedFeaturesInCollection( directSubclasses.map[EStructuralFeatures].flatten.toList, [existing, current| new EdeltaFeatureEqualityHelper().equals(existing, current) ] ) // only entries with a mapped list of size = num of subclasses are duplicates val duplicates = candidates .filter[key, values | values.size == numOfSubclasses] if (!duplicates.empty) { map.put(c, duplicates) duplicates.entrySet.forEach[ logInfo[ "In subclasses of " + getEObjectRepr(c) + ", duplicate features: " + value.map[getEObjectRepr(it)].join(", ") ] ] } } } 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. */ def findRedundantContainers(EPackage ePackage) { ePackage.allEClasses .map[findRedundantContainers] .flatten } /** * see {@link #findRedundantContainers(EPackage)} */ def findRedundantContainers(EClass cl) { val redundantContainers = newArrayList val containmentReferences = cl.EReferences .filter[containment] for (containmentReference : containmentReferences) { val redundant = containmentReference.EReferenceType.EReferences .filter[ !containment && required && EOpposite === null && EReferenceType === cl ] .head if (redundant !== null){ redundantContainers += redundant -> containmentReference logInfo[ "Redundant container: " + getEObjectRepr(containmentReference) + " -> " + getEObjectRepr(redundant) ] }} return redundantContainers } /** * see {@link #isDeadClassifier(EClassifier)} */ def findDeadClassifiers(EPackage ePackage) { ePackage.EClassifiers .filter[isDeadClassifier] .toList } /** * Whether {@link #doesNotReferToClasses(EClassifier)} and * {@link #isNotReferredByClassifiers(EClassifier)} */ def isDeadClassifier(EClassifier cl) { val result = (cl.doesNotReferToClasses && cl.isNotReferredByClassifiers) if (result) logInfo["Dead classifier: " + getEObjectRepr(cl)] return result; } /** * Whether the passed EClassifier does not refer to any EClass */ def doesNotReferToClasses(EClassifier c) { CrossReferencer.find(newArrayList(c)) .keySet .filter(EClass) .empty } /** * Whether the passed EClassifier is not referred by EClassifiers. */ def isNotReferredByClassifiers(EClassifier cl) { UsageCrossReferencer.find(cl, cl.packagesToInspect) .empty } /** * 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. */ def findClassificationByHierarchy(EPackage ePackage) : Map> { val classification= ePackage.allEClasses .filter[ ESuperTypes.size == 1 && EStructuralFeatures.empty && isNotReferredByClassifiers ] .groupBy[ESuperTypes.head] .filter[base, subclasses|subclasses.size > 1] // only if there are at least 2 subclasses classification.entrySet.forEach[ logInfo["Classification by hierarchy: " + getEObjectRepr(key) + " - " + "subclasses[" + value.map[getEObjectRepr(it)].join(",") + "]" ] ] return classification } /** * Finds base classes that should be set as abstract, * since they have subclasses. */ def findConcreteAbstractMetaclasses(EPackage ePackage) { val classes= ePackage.allEClasses .filter[ cl | !cl.abstract && cl.hasSubclasses ] classes.forEach[ logInfo["Concrete abstract class: " + getEObjectRepr(it)] ] return classes; } def hasSubclasses(EClass cl) { !cl.directSubclasses.empty } def directSubclasses(EClass cl) { EcoreUtil.UsageCrossReferencer .find(cl, cl.packagesToInspect) .filter[EStructuralFeature === ecoreref(eSuperTypes)] .map[EObject as EClass] } /** * Finds abstract classes that should be concrete, * since they have no subclasses. */ def findAbstractConcreteMetaclasses(EPackage ePackage) { val classes= ePackage.allEClasses .filter[ cl | cl.abstract && !cl.hasSubclasses ] classes.forEach[ logInfo["Abstract concrete class: " + getEObjectRepr(it)] ] return classes; } /** * Finds classes that are abstract though they have only concrete superclasses. */ def findAbstractSubclassesOfConcreteSuperclasses(EPackage ePackage) { val classes= ePackage.allEClasses .filter[ cl | cl.abstract && !cl.ESuperTypes.empty && cl.ESuperTypes.forall[!abstract] ] classes.forEach[ logInfo["Abstract class with concrete superclasses: " + getEObjectRepr(it)] ] return classes; }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy