-
Notifications
You must be signed in to change notification settings - Fork 1
Implemented draft of RMMR algorithm. #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
RamSaw
wants to merge
43
commits into
master
Choose a base branch
from
RMMR_implementation
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 9 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
174fcd2
Implemented draft of RMMR algorithm.
RamSaw 6f61018
Made several changes: corrected wrong behaviour because of missing br…
RamSaw 0fd31dd
Corrected test: it ran all algorithms, but assertions for RMMR algo a…
RamSaw bc85cad
Corrected marks from pull request.
RamSaw df7b1e0
Added tests for MovieRentalStore example.
RamSaw 08536b2
Added javadocs and made code clean up.
RamSaw 538ff85
Changed to run parallel. Parallel executing is not comfortable for de…
RamSaw 59b19a2
Made RMMR constructor public. IDEA says it can be package-private but…
RamSaw 1fce0bc
Made RmmrEntitySearcher more consistent and turned to qualified names.
RamSaw 79ebe6b
Moved 0.01 hardcoded constant to static final constant.
RamSaw 22ac2b6
Removed new expressions, added Builders, Utils, Factory cases, change…
RamSaw e227e2b
Added contextual similarity calculation. Very raw version. TODO: opti…
RamSaw 2d0ddd0
Fixed null pointer exception by change get to getOrDefault. Also adde…
RamSaw e9f71fd
Changed to multisets and all maps from entity moved to entity class t…
RamSaw 00333f5
Deleted redundant documents sustaining. Now it consists of two refere…
RamSaw 2c4d416
Added support of configuration of algorithm.
RamSaw c1379f4
Added stemming.
RamSaw 2400fb5
Reverted ArchitectureReloaded.iml file.
RamSaw 66c2b2c
fix conflicts commit
RamSaw 826ee53
Added jar for stemming
RamSaw 83168b2
Removed qualified name error fix because tests were not passing.
RamSaw 9eef351
Merge branch 'RMMR_implementation' of https://github.com/ml-in-progra…
RamSaw 42a7471
Erased bug with qualified names and added excluding short names like …
RamSaw 08740ec
Added tests for RMMR and added support for setting up or not field re…
RamSaw ebe7152
Speeded up algorithm by adding coordinates that are only not null. Th…
RamSaw b9e8966
Fixed tests. Failing test were uncommented by mistake.
RamSaw 28153b7
Algorithm was optimized more, now bag finder class deleted and its lo…
RamSaw d2f1015
Found mistake by dividing on zero. Fixed and as a consequence move me…
RamSaw c0503ef
RMMR was set up to pass more test cases. On real project output is no…
RamSaw 9f884cd
Ignored tests in RmmrDistancesTest and RmmrEntitySearcherTest. Reason…
RamSaw 5cd9df9
Fixed tests failure. Ignored tests in RmmrDistancesTest and RmmrEntit…
RamSaw 82ade8e
Added support for method references: ClassA::methodInClassA.
RamSaw bc238ca
Added support for class references. For example call of static method…
RamSaw a342283
Added failure explanations for all tests which fail.
RamSaw f15f724
Corrected weights for conceptual and contextual similarity.
RamSaw 9bd97d4
Corrected accuracy formula - now output is a little bit better.
RamSaw fd94e63
Made clean up of code, code prepared for review. Deleted old test data.
RamSaw 3d86756
Corrected accuracy formula to fit other algorithms. Before that accur…
RamSaw afa2096
Fixes #50
RamSaw 9fc5780
Fixed test failure.
RamSaw ac4cf63
Reverted bug fix, because it introduced new bug.
RamSaw bd370e9
Merge remote-tracking branch 'origin/master' into RMMR_implementation
RamSaw c183397
Merge remote-tracking branch 'origin/master' into RMMR_implementation
RamSaw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,9 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <module relativePaths="true" type="JAVA_MODULE" version="4"> | ||
| <component name="NewModuleRootManager" inherit-compiler-output="false"> | ||
| <output url="file://$MODULE_DIR$/build/classes" /> | ||
| <output-test url="file://$MODULE_DIR$/build/testclasses" /> | ||
| <module external.linked.project.id="openapi" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4"> | ||
| <component name="NewModuleRootManager" inherit-compiler-output="true"> | ||
| <exclude-output /> | ||
| <content url="file://$MODULE_DIR$"> | ||
| <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> | ||
| <excludeFolder url="file://$MODULE_DIR$/build" /> | ||
| </content> | ||
| <content url="file://$MODULE_DIR$" /> | ||
| <orderEntry type="inheritedJdk" /> | ||
| <orderEntry type="sourceFolder" forTests="false" /> | ||
| </component> | ||
| </module> | ||
|
|
||
| </module> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| package org.ml_methods_group.algorithm; | ||
|
|
||
| import org.apache.log4j.Logger; | ||
| import org.ml_methods_group.algorithm.entity.ClassEntity; | ||
| import org.ml_methods_group.algorithm.entity.EntitySearchResult; | ||
| import org.ml_methods_group.algorithm.entity.MethodEntity; | ||
| import org.ml_methods_group.config.Logging; | ||
| import org.ml_methods_group.utils.AlgorithmsUtil; | ||
|
|
||
| import java.util.*; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| /** | ||
| * Implementation of RMMR (Recommendation of Move Method Refactoring) algorithm. | ||
| * Based on @see <a href="https://drive.google.com/file/d/17yAlVXRaLuhIcXB4PEzNiZj5p1oi4HlL/view">this article</a>. | ||
| */ | ||
| public class RMMR extends Algorithm { | ||
| /** | ||
| * Internal name of the algorithm in the program. | ||
| */ | ||
| public static final String NAME = "RMMR"; | ||
| private static final Logger LOGGER = Logging.getLogger(RMMR.class); | ||
|
|
||
| /** | ||
| * Map: class -> set of method in this class. | ||
| */ | ||
| private final Map<ClassEntity, Set<MethodEntity>> methodsByClass = new HashMap<>(); | ||
| /** | ||
| * Methods to check for refactoring. | ||
| */ | ||
| private final List<MethodEntity> units = new ArrayList<>(); | ||
| /** | ||
| * Classes to which method will be considered for moving. | ||
| */ | ||
| private final List<ClassEntity> classEntities = new ArrayList<>(); | ||
| private final AtomicInteger progressCount = new AtomicInteger(); | ||
| /** | ||
| * Context which stores all found classes, methods and its metrics (by storing Entity). | ||
| */ | ||
| private ExecutionContext context; | ||
|
|
||
| public RMMR() { | ||
| super(NAME, true); | ||
| } | ||
|
|
||
| @Override | ||
| protected List<Refactoring> calculateRefactorings(ExecutionContext context, boolean enableFieldRefactorings) { | ||
| if (enableFieldRefactorings) { | ||
| // TODO: write to LOGGER or throw Exception? Change UI: disable field checkbox if only RMMR is chosen. | ||
| LOGGER.error("Field refactorings are not supported", | ||
| new UnsupportedOperationException("Field refactorings are not supported")); | ||
| } | ||
| this.context = context; | ||
| init(); | ||
|
|
||
| /* | ||
| List<Refactoring> accum = new LinkedList<>(); | ||
| units.forEach(methodEntity -> findRefactoring(methodEntity, accum)); | ||
| return accum; | ||
| */ | ||
| return runParallel(units, context, ArrayList::new, this::findRefactoring, AlgorithmsUtil::combineLists); | ||
| } | ||
|
|
||
| /** | ||
| * Initializes units, methodsByClass, classEntities. Data is gathered from context.getEntities(). | ||
| */ | ||
| private void init() { | ||
| final EntitySearchResult entities = context.getEntities(); | ||
| LOGGER.info("Init RMMR"); | ||
| units.clear(); | ||
| classEntities.clear(); | ||
| methodsByClass.clear(); | ||
|
|
||
| classEntities.addAll(entities.getClasses()); | ||
| units.addAll(entities.getMethods()); | ||
| progressCount.set(0); | ||
|
|
||
| entities.getMethods().forEach(methodEntity -> { | ||
| List<ClassEntity> methodClassEntity = entities.getClasses().stream() | ||
| .filter(classEntity -> methodEntity.getClassName().equals(classEntity.getName())) | ||
| .collect(Collectors.toList()); | ||
| if (methodClassEntity.size() != 1) { | ||
| LOGGER.error("Found more than 1 class that has this method"); | ||
| } | ||
| methodsByClass.computeIfAbsent(methodClassEntity.get(0), anyKey -> new HashSet<>()).add(methodEntity); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Methods decides whether to move method or not, based on calculating distances between given method and classes. | ||
| * | ||
| * @param entity method to check for move method refactoring. | ||
| * @param accumulator list of refactorings, if method must be moved, refactoring for it will be added to this accumulator. | ||
| * @return changed or unchanged accumulator. | ||
| */ | ||
| private List<Refactoring> findRefactoring(MethodEntity entity, List<Refactoring> accumulator) { | ||
| reportProgress((double) progressCount.incrementAndGet() / units.size(), context); | ||
| context.checkCanceled(); | ||
| if (!entity.isMovable() || classEntities.size() < 2) { | ||
| return accumulator; | ||
| } | ||
| double minDistance = Double.POSITIVE_INFINITY; | ||
| double difference = Double.POSITIVE_INFINITY; | ||
| double distanceWithSourceClass = 1; | ||
| ClassEntity targetClass = null; | ||
| ClassEntity sourceClass = null; | ||
| for (final ClassEntity classEntity : classEntities) { | ||
| final double distance = getDistance(entity, classEntity); | ||
| if (classEntity.getName().equals(entity.getClassName())) { | ||
| sourceClass = classEntity; | ||
| distanceWithSourceClass = distance; | ||
| } | ||
| if (distance < minDistance) { | ||
| difference = minDistance - distance; | ||
| minDistance = distance; | ||
| targetClass = classEntity; | ||
| } else if (distance - minDistance < difference) { | ||
| difference = distance - minDistance; | ||
| } | ||
| } | ||
|
|
||
| if (targetClass == null) { | ||
| LOGGER.warn("targetClass is null for " + entity.getName()); | ||
| return accumulator; | ||
| } | ||
| final String targetClassName = targetClass.getName(); | ||
| double differenceWithSourceClass = distanceWithSourceClass - minDistance; | ||
| int numberOfMethodsInSourceClass = methodsByClass.get(sourceClass).size(); | ||
| int numberOfMethodsInTargetClass = methodsByClass.get(targetClass).size(); | ||
| // considers amount of entities. | ||
| double sourceClassCoefficient = 1 - 1.0 / (2 * numberOfMethodsInSourceClass * numberOfMethodsInSourceClass); | ||
| double targetClassCoefficient = 1 - 1.0 / (4 * numberOfMethodsInTargetClass * numberOfMethodsInTargetClass); | ||
| double differenceWithSourceClassCoefficient = (1 - minDistance) * differenceWithSourceClass; | ||
| double accuracy = (0.7 * differenceWithSourceClassCoefficient + 0.3 * difference) * | ||
| sourceClassCoefficient * targetClassCoefficient; | ||
| // accuracy = 1; | ||
| if (accuracy >= 0.01 && !targetClassName.equals(entity.getClassName())) { | ||
| accumulator.add(new Refactoring(entity.getName(), targetClassName, accuracy, entity.isField())); | ||
| } | ||
| return accumulator; | ||
| } | ||
|
|
||
| /** | ||
| * Measures distance (a number in [0; 1]) between method and a class. | ||
| * It is an average of distances between method and class methods. | ||
| * If there is no methods in a given class then distance is 1. | ||
| * @param methodEntity method to calculate distance. | ||
| * @param classEntity class to calculate distance. | ||
| * @return distance between the method and the class. | ||
| */ | ||
| private double getDistance(MethodEntity methodEntity, ClassEntity classEntity) { | ||
| int number = 0; | ||
| double sumOfDistances = 0; | ||
|
|
||
| if (methodsByClass.containsKey(classEntity)) { | ||
| for (MethodEntity methodEntityInClass : methodsByClass.get(classEntity)) { | ||
| if (!methodEntity.equals(methodEntityInClass)) { | ||
| sumOfDistances += getDistance(methodEntity, methodEntityInClass); | ||
| number++; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return number == 0 ? 1 : sumOfDistances / number; | ||
| } | ||
|
|
||
| /** | ||
| * Measures distance (a number in [0; 1]) between two methods. | ||
| * It is sizeOfIntersection(A1, A2) / sizeOfUnion(A1, A2), where Ai is a conceptual set of method. | ||
| * If A1 and A2 are empty then distance is 1. | ||
| * @param methodEntity1 method to calculate distance. | ||
| * @param methodEntity2 method to calculate distance. | ||
| * @return distance between two given methods. | ||
| */ | ||
| private double getDistance(MethodEntity methodEntity1, MethodEntity methodEntity2) { | ||
| // TODO: Maybe add to methodEntity2 source class where it is located? | ||
| Set<String> method1Classes = methodEntity1.getRelevantProperties().getClasses(); | ||
| Set<String> method2Classes = methodEntity2.getRelevantProperties().getClasses(); | ||
| int sizeOfIntersection = intersection(method1Classes, method2Classes).size(); | ||
| int sizeOfUnion = union(method1Classes, method2Classes).size(); | ||
| return (sizeOfUnion == 0) ? 1 : 1 - (double) sizeOfIntersection / sizeOfUnion; | ||
| } | ||
|
|
||
| private <T> Set<T> intersection(Set<T> set1, Set<T> set2) { | ||
| Set<T> intersection = new HashSet<>(set1); | ||
| intersection.retainAll(set2); | ||
| return intersection; | ||
| } | ||
|
|
||
| private <T> Set<T> union(Set<T> set1, Set<T> set2) { | ||
| Set<T> union = new HashSet<>(set1); | ||
| union.addAll(set2); | ||
| return union; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
accuracy >= 0.01is it just a comparison with epsilon?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to final static constant.