Skip to content
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

MOS-1864: Fix partial pattern application in matcher #35

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ Set<Node> match(final Pattern pattern) {
);
final Set<Node> set = new HashSet<>();
for (final Node node : preset) {
final boolean matches = this.checkNode(node, null, head);
final ActionList applicants = new ActionList();
final boolean matches = Matcher.checkNode(node, head, applicants);
if (matches) {
set.add(node);
this.actions.merge(applicants);
}
}
return set;
Expand All @@ -97,12 +99,12 @@ Set<Node> match(final Pattern pattern) {
/**
* Checks if the node of the original tree matches the pattern node.
* @param node Node of the original tree
* @param parent Parent node of the node being checked.
* @param pattern Node of the difference tree (i.e. pattern)
* @param actions List of actions to be performed to apply the pattern
* @return Matching result ({@code true} if matches)
*/
@SuppressWarnings("PMD.UnusedFormalParameter")
private boolean checkNode(final Node node, final Node parent, final Node pattern) {
private static boolean checkNode(
final Node node, final Node pattern, final ActionList actions) {
final Node sample;
final Action action = Action.toAction(pattern);
if (action instanceof Replace || action instanceof Delete) {
Expand All @@ -113,12 +115,13 @@ private boolean checkNode(final Node node, final Node parent, final Node pattern
boolean result = node.getTypeName().equals(sample.getTypeName());
if (!(pattern instanceof Hole)) {
result = result && node.getData().equals(sample.getData());
result = result && (node.getChildCount() == 0 || this.checkChildren(node, sample));
result = result && (node.getChildCount() == 0
|| Matcher.checkChildren(node, sample, actions));
}
if (result && action instanceof Replace) {
this.actions.replaceNode(node, action.getAfter());
actions.replaceNode(node, action.getAfter());
} else if (result & action instanceof Delete) {
this.actions.deleteNode(node);
actions.deleteNode(node);
}
return result;
}
Expand All @@ -128,12 +131,15 @@ private boolean checkNode(final Node node, final Node parent, final Node pattern
* matches the children of the pattern node.
* @param node Node of the original tree
* @param sample Node of the difference tree (i.e. pattern)
* @param actions List of actions to be performed to apply the pattern
* @return Matching result ({@code true} if matches)
*/
private boolean checkChildren(final Node node, final Node sample) {
private static boolean checkChildren(
final Node node, final Node sample, final ActionList actions) {
final int left = node.getChildCount();
final int right = sample.getChildCount();
boolean result = false;
final ActionList applicants = new ActionList();
for (int index = 0; !result && index < left; index = index + 1) {
result = true;
final Iterator<Node> iterator = sample.getIteratorOverChildren();
Expand All @@ -143,20 +149,23 @@ private boolean checkChildren(final Node node, final Node sample) {
final Node child = iterator.next();
final Action action = Action.toAction(child);
if (action instanceof Insert) {
this.actions.insertNodeAfter(action.getAfter(), node, current);
applicants.insertNodeAfter(action.getAfter(), node, current);
} else if (index + offset >= left) {
result = false;
} else {
current = node.getChild(index + offset);
result = this.checkNode(
result = Matcher.checkNode(
current,
node,
child
child,
applicants
);
offset = offset + 1;
}
}
}
if (result) {
actions.merge(applicants);
}
return result;
}
}
17 changes: 14 additions & 3 deletions src/main/java/org/cqfn/astranaut/core/base/ActionList.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
*/
package org.cqfn.astranaut.core.base;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
Expand All @@ -33,14 +35,13 @@

/**
* List of actions to be added to the tree after deserialization to produce a difference tree.
*
* @since 1.1.0
*/
public final class ActionList {
/**
* Collection of nodes to be inserted.
*/
private final Set<Insertion> insert;
private final List<Insertion> insert;

/**
* Collection of nodes to be replaced (node before changes -> node after changes).
Expand All @@ -56,7 +57,7 @@ public final class ActionList {
* Constructor.
*/
public ActionList() {
this.insert = new HashSet<>();
this.insert = new ArrayList<>(0);
this.replace = new HashMap<>();
this.delete = new HashSet<>();
}
Expand Down Expand Up @@ -109,6 +110,16 @@ public void deleteNode(final Node node) {
this.delete.add(node);
}

/**
* Adds actions from another list to the current list.
* @param other Another action list
*/
public void merge(final ActionList other) {
this.insert.addAll(other.insert);
this.replace.putAll(other.replace);
this.delete.addAll(other.delete);
}

/**
* Converts the tree to a difference tree using the list of actions.
* @param tree Source tree
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@
import java.util.Set;
import java.util.TreeMap;
import org.cqfn.astranaut.core.algorithms.DiffTreeBuilder;
import org.cqfn.astranaut.core.algorithms.PatternBuilder;
import org.cqfn.astranaut.core.algorithms.mapping.Mapper;
import org.cqfn.astranaut.core.algorithms.mapping.TopDownMapper;
import org.cqfn.astranaut.core.base.Builder;
import org.cqfn.astranaut.core.base.DiffNode;
import org.cqfn.astranaut.core.base.DiffTree;
import org.cqfn.astranaut.core.base.DraftNode;
import org.cqfn.astranaut.core.base.Insertion;
import org.cqfn.astranaut.core.base.Node;
Expand Down Expand Up @@ -186,4 +190,26 @@ void patchWithPatternThatDoesNotMatch() {
result = patcher.patch(tree, third);
Assertions.assertTrue(tree.deepCompare(result));
}

@Test
void mineAndPatch() {
final Node before = DraftNode.create("X(A,C(D(F(G(H)))))");
final Node after = DraftNode.create("X(A,B,C(D(F(G(I)))))");
final Mapper mapper = TopDownMapper.INSTANCE;
final DiffTreeBuilder diffbuilder = new DiffTreeBuilder(before);
diffbuilder.build(after, mapper);
final DiffTree diff = diffbuilder.getDiffTree();
Assertions.assertTrue(before.deepCompare(diff.getBefore().getRoot()));
Assertions.assertTrue(after.deepCompare(diff.getAfter().getRoot()));
final Pattern pattern = new PatternBuilder(diff).getPattern();
final Tree original = Tree.createDraft(
"Y(X(A,C(D(F(G(J,K))))),X(A,C(D(F(G(L))))),X(A,C(D(F(G(H))))))"
);
final Patcher patcher = DefaultPatcher.INSTANCE;
final Tree patched = patcher.patch(original, pattern);
final Tree expected = Tree.createDraft(
"Y(X(A,C(D(F(G(J,K))))),X(A,C(D(F(G(L))))),X(A,B,C(D(F(G(I))))))"
);
Assertions.assertEquals(expected.toString(), patched.toString());
}
}
Loading