From 0927bd06bd3444e24e5057252ea65c1e3c39195c Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Wed, 27 Mar 2024 14:48:53 +0300 Subject: [PATCH 01/14] Top-down mapper: implement mapper for case where the only actions that are allowed are insertions. --- .../algorithms/mapping/TopDownAlgorithm.java | 32 ++++++++++++++++--- .../algorithms/mapping/TopDownMapper.java | 11 +++++++ .../algorithms/mapping/TopDownMapperTest.java | 20 +++++++++++- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index 8609c81..926ee73 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -100,7 +100,7 @@ boolean execute(final Node left, final Node right) { if (result) { this.ltr.put(left, right); this.rtl.put(right, left); - TopDownAlgorithm.mapSubtreesWithDifferentHashes(left, right); + this.mapSubtreesWithDifferentHashes(left, right); } } return result; @@ -137,9 +137,31 @@ private void mapSubtreesWithTheSameHash(final Node left, final Node right) { * @param left Left node * @param right Related node to the left node */ - private static void mapSubtreesWithDifferentHashes(final Node left, final Node right) { + private void mapSubtreesWithDifferentHashes(final Node left, final Node right) { final Unprocessed counter = new Unprocessed(left, right); assert counter.hasUnprocessedNodes(); + if (counter.onlyActionIsToInsertNodes()) { + this.insertAllNotYetMappedNodes(right); + } + } + + /** + * For all child nodes of the right node that are not yet mapped, performs an insert operation. + * @param right Related node to the left node + */ + private void insertAllNotYetMappedNodes(final Node right) { + final int count = right.getChildCount(); + Node after = null; + for (int index = 0; index < count; index = index + 1) { + final Node node = right.getChild(index); + if (this.rtl.containsKey(node)) { + after = this.rtl.get(node); + } else { + final Insertion insertion = new Insertion(node, right, after); + this.inserted.add(insertion); + after = node; + } + } } /** @@ -230,14 +252,14 @@ private static class Unprocessed { * @return Checking result ({@code true} if yes) */ boolean hasUnprocessedNodes() { - return this.left > 0 && this.right > 0; + return this.left > 0 || this.right > 0; } /** - * Analyzes a case where the only actions that are allowed are additions. + * Analyzes a case where the only actions that are allowed are insertions. * @return Checking result, {@code true} if we can only add nodes */ - boolean onlyActionIsToAddNodes() { + boolean onlyActionIsToInsertNodes() { return this.left == 0 && this.add == this.right; } diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapper.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapper.java index 78da7e5..d19ff2e 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapper.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapper.java @@ -32,6 +32,17 @@ * @since 1.1.0 */ public final class TopDownMapper implements Mapper { + /** + * The instance. + */ + public static final Mapper INSTANCE = new TopDownMapper(); + + /** + * Private constructor. + */ + private TopDownMapper() { + } + @Override public Mapping map(final Node left, final Node right) { final TopDownAlgorithm algorithm = new TopDownAlgorithm(); diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 7e49599..d6965e5 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -23,7 +23,11 @@ */ package org.cqfn.astranaut.core.algorithms.mapping; +import java.util.Arrays; +import java.util.List; +import java.util.Set; import org.cqfn.astranaut.core.DraftNode; +import org.cqfn.astranaut.core.Insertion; import org.cqfn.astranaut.core.Node; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -39,9 +43,23 @@ void testIdenticalTrees() { final String description = "A(B(C, D))"; final Node first = DraftNode.createByDescription(description); final Node second = DraftNode.createByDescription(description); - final Mapper mapper = new TopDownMapper(); + final Mapper mapper = TopDownMapper.INSTANCE; final Mapping mapping = mapper.map(first, second); Assertions.assertEquals(mapping.getRight(first), second); Assertions.assertEquals(mapping.getLeft(second), first); } + + @Test + void testPairOfTreesWhereOnlyInsertion() { + final Node first = DraftNode.createByDescription("X()"); + final Node second = DraftNode.createByDescription("X(A,B)"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final List inserted = Arrays.asList("A", "B"); + final Set set = mapping.getInserted(); + for (final Insertion insertion : set) { + final String name = insertion.getNode().getTypeName(); + Assertions.assertTrue(inserted.contains(name)); + } + } } From 828a88d36ed00b2fca1293c2b9754ba9931cbece Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Thu, 28 Mar 2024 15:08:39 +0300 Subject: [PATCH 02/14] Top-down mapper: implement mapper for the case where some nodes are existing and some are added --- .../algorithms/mapping/TopDownAlgorithm.java | 57 ++++++++++++++++--- .../algorithms/mapping/TopDownMapperTest.java | 11 ++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index 926ee73..fdddea7 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -138,11 +138,53 @@ private void mapSubtreesWithTheSameHash(final Node left, final Node right) { * @param right Related node to the left node */ private void mapSubtreesWithDifferentHashes(final Node left, final Node right) { - final Unprocessed counter = new Unprocessed(left, right); - assert counter.hasUnprocessedNodes(); - if (counter.onlyActionIsToInsertNodes()) { - this.insertAllNotYetMappedNodes(right); + final Unprocessed unprocessed = new Unprocessed(left, right); + assert unprocessed.hasNodes(); + do { + if (unprocessed.onlyActionIsToInsertNodes()) { + this.insertAllNotYetMappedNodes(right); + break; + } + this.mapTwoFirstUnmappedNodes(left, right, unprocessed); + } while (unprocessed.hasNodes()); + } + + /** + * Finds the first unmapped child of the left node and the first unmapped child + * of the right node and tries to map them. + * @param left Left node + * @param right Related node to the left node + * @param unprocessed Number of unprocessed nodes + * @return Mapping result, {@code true} if such nodes were found and mapped + */ + private boolean mapTwoFirstUnmappedNodes(final Node left, final Node right, + final Unprocessed unprocessed) { + final Node first = this.findFirstUnmappedChild(left); + final Node second = this.findFirstUnmappedChild(right); + final boolean result = this.execute(first, second); + if (result) { + unprocessed.removeOnePair(); + } + return result; + } + + /** + * Finds the first child node that has not yet been mapped. + * @param node Parent node + * @return First child node that has not yet been mapped + */ + private Node findFirstUnmappedChild(final Node node) { + final int count = node.getChildCount(); + Node result = null; + for (int index = 0; index < count; index = index + 1) { + final Node child = node.getChild(index); + if (!this.ltr.containsKey(child) && !this.rtl.containsKey(child)) { + result = child; + break; + } } + assert result != null; + return result; } /** @@ -251,7 +293,7 @@ private static class Unprocessed { * Checks are there still unprocessed nodes. * @return Checking result ({@code true} if yes) */ - boolean hasUnprocessedNodes() { + boolean hasNodes() { return this.left > 0 || this.right > 0; } @@ -272,9 +314,10 @@ boolean onlyActionIsToDeleteNodes() { } /** - * Marks that some child of the left node has been replaced by a child of the right node. + * Marks that some child of the left node has been mapped or replaced by a child + * of the right node. */ - void nodeWasReplaced() { + void removeOnePair() { this.left = this.left - 1; this.right = this.right - 1; assert this.right >= this.add; diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index d6965e5..fa76ec7 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -62,4 +62,15 @@ void testPairOfTreesWhereOnlyInsertion() { Assertions.assertTrue(inserted.contains(name)); } } + + @Test + void testPairOfTreesWhereOneAndOneInserted() { + final Node first = DraftNode.createByDescription("X(A)"); + final Node second = DraftNode.createByDescription("X(A,B)"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set inserted = mapping.getInserted(); + Assertions.assertEquals(1, inserted.size()); + Assertions.assertEquals("B", inserted.iterator().next().getNode().getTypeName()); + } } From b82cdd7d69809c07b0543dab2bf10a57ffa2a199 Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Thu, 28 Mar 2024 15:21:03 +0300 Subject: [PATCH 03/14] Top-down mapper: implement mapper for when some node is inserted into the middle --- .../algorithms/mapping/TopDownAlgorithm.java | 43 ++++++++++++++++++- .../algorithms/mapping/TopDownMapperTest.java | 11 +++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index fdddea7..11d54d3 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -145,7 +145,10 @@ private void mapSubtreesWithDifferentHashes(final Node left, final Node right) { this.insertAllNotYetMappedNodes(right); break; } - this.mapTwoFirstUnmappedNodes(left, right, unprocessed); + if (this.mapTwoFirstUnmappedNodes(left, right, unprocessed)) { + continue; + } + this.mapTwoLastUnmappedNodes(left, right, unprocessed); } while (unprocessed.hasNodes()); } @@ -187,6 +190,44 @@ private Node findFirstUnmappedChild(final Node node) { return result; } + /** + * Finds the last unmapped child of the left node and the last unmapped child + * of the right node and tries to map them. + * @param left Left node + * @param right Related node to the left node + * @param unprocessed Number of unprocessed nodes + * @return Mapping result, {@code true} if such nodes were found and mapped + */ + private boolean mapTwoLastUnmappedNodes(final Node left, final Node right, + final Unprocessed unprocessed) { + final Node first = this.findLastUnmappedChild(left); + final Node second = this.findLastUnmappedChild(right); + final boolean result = this.execute(first, second); + if (result) { + unprocessed.removeOnePair(); + } + return result; + } + + /** + * Finds the last child node that has not yet been mapped. + * @param node Parent node + * @return Last child node that has not yet been mapped + */ + private Node findLastUnmappedChild(final Node node) { + final int count = node.getChildCount(); + Node result = null; + for (int index = count - 1; index >= 0; index = index - 1) { + final Node child = node.getChild(index); + if (!this.ltr.containsKey(child) && !this.rtl.containsKey(child)) { + result = child; + break; + } + } + assert result != null; + return result; + } + /** * For all child nodes of the right node that are not yet mapped, performs an insert operation. * @param right Related node to the left node diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index fa76ec7..d08ed25 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -73,4 +73,15 @@ void testPairOfTreesWhereOneAndOneInserted() { Assertions.assertEquals(1, inserted.size()); Assertions.assertEquals("B", inserted.iterator().next().getNode().getTypeName()); } + + @Test + void testPairOfTreesWhereTwoAndOneInserted() { + final Node first = DraftNode.createByDescription("X(A,C)"); + final Node second = DraftNode.createByDescription("X(A,B,C)"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set inserted = mapping.getInserted(); + Assertions.assertEquals(1, inserted.size()); + Assertions.assertEquals("B", inserted.iterator().next().getNode().getTypeName()); + } } From 97753895dd96da92c936b3086f8f32b2ff40fd2c Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Thu, 28 Mar 2024 15:27:42 +0300 Subject: [PATCH 04/14] + 1 amazing test --- .../algorithms/mapping/TopDownMapperTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index d08ed25..ac3b797 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -84,4 +84,19 @@ void testPairOfTreesWhereTwoAndOneInserted() { Assertions.assertEquals(1, inserted.size()); Assertions.assertEquals("B", inserted.iterator().next().getNode().getTypeName()); } + + @Test + void testNodeInsertedAmongIdenticalNodes() { + final Node first = DraftNode.createByDescription("X(A,A,A,A,C)"); + final Node second = DraftNode.createByDescription("X(A,A,A,B,A,C)"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set inserted = mapping.getInserted(); + Assertions.assertEquals(1, inserted.size()); + Assertions.assertEquals("B", inserted.iterator().next().getNode().getTypeName()); + Assertions.assertEquals(second.getChild(0), mapping.getRight(first.getChild(0))); + Assertions.assertEquals(second.getChild(1), mapping.getRight(first.getChild(1))); + Assertions.assertEquals(second.getChild(2), mapping.getRight(first.getChild(2))); + Assertions.assertEquals(second.getChild(4), mapping.getRight(first.getChild(3))); + } } From 78e94becb0228973a65e2b541c42a241fc291246 Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Fri, 29 Mar 2024 09:57:37 +0300 Subject: [PATCH 05/14] Top-down mapper: implement mapper for the case where some nodes are existing and some are deleted --- .../algorithms/mapping/TopDownAlgorithm.java | 22 ++++++++++++++++++- .../algorithms/mapping/TopDownMapperTest.java | 11 ++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index 11d54d3..7fdfca3 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -145,6 +145,10 @@ private void mapSubtreesWithDifferentHashes(final Node left, final Node right) { this.insertAllNotYetMappedNodes(right); break; } + if (unprocessed.onlyActionIsToDeleteNodes()) { + this.deleteAllNotYetMappedNodes(left); + break; + } if (this.mapTwoFirstUnmappedNodes(left, right, unprocessed)) { continue; } @@ -229,7 +233,8 @@ private Node findLastUnmappedChild(final Node node) { } /** - * For all child nodes of the right node that are not yet mapped, performs an insert operation. + * For all child nodes of the right node that are not yet mapped, performs + * the 'Insert' operation. * @param right Related node to the left node */ private void insertAllNotYetMappedNodes(final Node right) { @@ -247,6 +252,21 @@ private void insertAllNotYetMappedNodes(final Node right) { } } + /** + * For all child nodes of the left node that are not yet mapped, performs + * the 'Delete' operation. + * @param left Related node to the left node + */ + private void deleteAllNotYetMappedNodes(final Node left) { + final int count = left.getChildCount(); + for (int index = 0; index < count; index = index + 1) { + final Node node = left.getChild(index); + if (!this.ltr.containsKey(node)) { + this.deleted.add(node); + } + } + } + /** * Mapping result. * diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index ac3b797..51d4225 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -99,4 +99,15 @@ void testNodeInsertedAmongIdenticalNodes() { Assertions.assertEquals(second.getChild(2), mapping.getRight(first.getChild(2))); Assertions.assertEquals(second.getChild(4), mapping.getRight(first.getChild(3))); } + + @Test + void testPairOfTreesWhereTwoAndOneDeleted() { + final Node first = DraftNode.createByDescription("X(A,B)"); + final Node second = DraftNode.createByDescription("X(A)"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set deleted = mapping.getDeleted(); + Assertions.assertEquals(1, deleted.size()); + Assertions.assertEquals("B", deleted.iterator().next().getTypeName()); + } } From dd04a894b5d55d8ca6f31c8282f432725bbc84c2 Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Fri, 29 Mar 2024 14:48:15 +0300 Subject: [PATCH 06/14] Top-down mapper: perform "Replace" operation --- .../algorithms/mapping/TopDownAlgorithm.java | 28 ++++++++++++++++++- .../algorithms/mapping/TopDownMapperTest.java | 14 ++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index 7fdfca3..5249080 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -152,7 +152,10 @@ private void mapSubtreesWithDifferentHashes(final Node left, final Node right) { if (this.mapTwoFirstUnmappedNodes(left, right, unprocessed)) { continue; } - this.mapTwoLastUnmappedNodes(left, right, unprocessed); + if (this.mapTwoLastUnmappedNodes(left, right, unprocessed)) { + continue; + } + this.replaceTwoFirstUnmappedNodes(left, right, unprocessed); } while (unprocessed.hasNodes()); } @@ -175,6 +178,27 @@ private boolean mapTwoFirstUnmappedNodes(final Node left, final Node right, return result; } + /** + * Finds the first unmapped child of the left node and the first unmapped child + * of the right node and adds a 'Replace' operation for them. + * This is a universal operation because it reduces the number of unprocessed pairs, + * and sooner or later there will be no nodes left and the algorithm will inevitably + * terminate with some result. This is fate. However, this operation may produce + * suboptimal results, and should therefore be used last. + * @param left Left node + * @param right Related node to the left node + * @param unprocessed Number of unprocessed nodes + */ + private void replaceTwoFirstUnmappedNodes(final Node left, final Node right, + final Unprocessed unprocessed) { + final Node first = this.findFirstUnmappedChild(left); + final Node second = this.findFirstUnmappedChild(right); + this.replaced.put(first, second); + this.ltr.put(first, null); + this.rtl.put(second, null); + unprocessed.removeOnePair(); + } + /** * Finds the first child node that has not yet been mapped. * @param node Parent node @@ -247,6 +271,7 @@ private void insertAllNotYetMappedNodes(final Node right) { } else { final Insertion insertion = new Insertion(node, right, after); this.inserted.add(insertion); + this.rtl.put(node, null); after = node; } } @@ -263,6 +288,7 @@ private void deleteAllNotYetMappedNodes(final Node left) { final Node node = left.getChild(index); if (!this.ltr.containsKey(node)) { this.deleted.add(node); + this.ltr.put(node, null); } } } diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 51d4225..9deffa6 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; import org.cqfn.astranaut.core.DraftNode; import org.cqfn.astranaut.core.Insertion; @@ -110,4 +111,17 @@ void testPairOfTreesWhereTwoAndOneDeleted() { Assertions.assertEquals(1, deleted.size()); Assertions.assertEquals("B", deleted.iterator().next().getTypeName()); } + + @Test + void testPairOfTreesWhereThreeAndOneReplaced() { + final Node first = DraftNode.createByDescription("X(A,B,C)"); + final Node second = DraftNode.createByDescription("X(A,D,C)"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Map replaced = mapping.getReplaced(); + Assertions.assertEquals(1, replaced.size()); + final Map.Entry pair = replaced.entrySet().iterator().next(); + Assertions.assertEquals("B", pair.getKey().getTypeName()); + Assertions.assertEquals("D", pair.getValue().getTypeName()); + } } From 7d2418541a2f0e401215746f2ec35b8b4963a6ca Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Mon, 1 Apr 2024 08:24:47 +0300 Subject: [PATCH 07/14] Remove old mapping algorithm --- .../algorithms/mapping/BottomUpAlgorithm.java | 468 ------------------ .../algorithms/mapping/BottomUpMapper.java | 42 -- .../core/algorithms/mapping/DraftMapping.java | 117 ----- .../algorithms/DifferenceTreeBuilderTest.java | 12 +- .../mapping/BottomUpMapperTest.java | 76 --- 5 files changed, 6 insertions(+), 709 deletions(-) delete mode 100644 src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpAlgorithm.java delete mode 100644 src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapper.java delete mode 100644 src/main/java/org/cqfn/astranaut/core/algorithms/mapping/DraftMapping.java delete mode 100644 src/test/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapperTest.java diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpAlgorithm.java deleted file mode 100644 index daa7f51..0000000 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpAlgorithm.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ivan Kniazkov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package org.cqfn.astranaut.core.algorithms.mapping; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import org.cqfn.astranaut.core.Insertion; -import org.cqfn.astranaut.core.Node; -import org.cqfn.astranaut.core.algorithms.Depth; -import org.cqfn.astranaut.core.algorithms.hash.AbsoluteHash; -import org.cqfn.astranaut.core.algorithms.hash.Hash; - -/** - * Bottom-up mapping algorithm. - * Tries to match leaf nodes first, and then subtrees containing leaf nodes, - * gradually increasing the size of the matched subtrees. - * - * @since 1.1.0 - */ -@SuppressWarnings("PMD.TooManyMethods") -class BottomUpAlgorithm { - /** - * Set of node hashes. - */ - private final Hash hashes; - - /** - * Set of node depths. - */ - private final Depth depth; - - /** - * Relationship of the nodes to their parents. - */ - private final Map parents; - - /** - * A set of nodes that have not yet been processed. - */ - private final Set unprocessed; - - /** - * Sorted nodes from the 'left' tree. - */ - private final List left; - - /** - * Sorted nodes from the 'right' tree. - */ - private final List right; - - /** - * Left-to-right mapping. - */ - private final Map ltr; - - /** - * Right-to-left mapping. - */ - private final Map rtl; - - /** - * Set containing inserted nodes. - */ - private final Set inserted; - - /** - * Map containing replaces nodes. - */ - private final Map replaced; - - /** - * Set of deleted nodes. - */ - private final Set deleted; - - /** - * Constructor. - * @param left Root node of the 'left' tree - * @param right Root node of the 'right' tree - */ - BottomUpAlgorithm(final Node left, final Node right) { - this.hashes = new AbsoluteHash(); - this.depth = new Depth(); - this.parents = new HashMap<>(); - this.unprocessed = new HashSet<>(); - this.left = this.createNodeList(left); - this.right = this.createNodeList(right); - this.ltr = new HashMap<>(); - this.rtl = new HashMap<>(); - this.inserted = new HashSet<>(); - this.replaced = new HashMap<>(); - this.deleted = new HashSet<>(); - } - - /** - * Performs the mapping. - */ - void execute() { - final DraftMapping draft = this.performInitialMapping(); - this.absorbLargestSubtrees(draft); - Node node = this.findPartiallyMappedLeftNode(); - while (node != null) { - node = this.mapPartiallyMappedLeftNode(node); - if (node == null) { - node = this.findPartiallyMappedLeftNode(); - } - } - } - - /** - * Returns result of mapping. - * @return Result of mapping - */ - Mapping getResult() { - return new Result(this); - } - - /** - * Creates an initial list of nodes suitable for processing from the tree. - * @param root The root of the tree - * @return List of nodes where leaves are placed first. - */ - private List createNodeList(final Node root) { - final List list = new LinkedList<>(); - this.createNodeList(root, null, list); - return list; - } - - /** - * Creates an initial set of nodes suitable for processing from the tree (recursive method). - * @param node The current node - * @param parent The current node parent - * @param list The resulting list - */ - private void createNodeList(final Node node, final Node parent, final List list) { - this.parents.put(node, parent); - node.forEachChild(child -> this.createNodeList(child, node, list)); - list.add(node); - final boolean added = this.unprocessed.add(node); - assert added; - } - - /** - * Performs hash calculation of nodes from the 'right' set. - * @return The hash relation to the set of nodes that have such a hash - */ - private Map> calculateRightHashes() { - final Map> result = new TreeMap<>(); - for (final Node node : this.right) { - final int hash = this.hashes.calculate(node); - final Set set = - result.computeIfAbsent(hash, k -> new HashSet<>()); - set.add(node); - } - return result; - } - - /** - * Performs initial (draft) node mapping. - * @return Relationships of nodes to lists of nodes to which they can be mapped to - */ - private DraftMapping performInitialMapping() { - final DraftMapping result = new DraftMapping(); - final Map> relation = this.calculateRightHashes(); - for (final Node node : this.left) { - final int hash = this.hashes.calculate(node); - final Set set = relation.get(hash); - if (set != null) { - result.addRelation(node, set); - } - } - return result; - } - - /** - * Selects the largest size subtrees from the initial node relation and maps them. - * @param draft Initial node relation - */ - private void absorbLargestSubtrees(final DraftMapping draft) { - final List sorted = new ArrayList<>(draft.getLeftNodes()); - sorted.sort( - (first, second) -> Integer.compare( - this.depth.calculate(second), - this.depth.calculate(first) - ) - ); - for (final Node node : sorted) { - final Set related = draft.getRelation(node); - if (related != null && related.size() == 1 && !this.ltr.containsKey(node)) { - this.mapSubtreesWithTheSameHash(node, related.iterator().next(), draft); - } - if (draft.isEmpty()) { - break; - } - } - } - - /** - * Maps subtrees with the same hash, adding the corresponding nodes to the resulting - * collections and removing them from the initial mapping. - * @param node Left node - * @param related Related node to the left node - * @param draft Initial node relation - */ - private void mapSubtreesWithTheSameHash( - final Node node, - final Node related, - final DraftMapping draft) { - assert this.hashes.calculate(node) == this.hashes.calculate(related); - draft.removeRelation(node, related); - this.unprocessed.remove(node); - this.unprocessed.remove(related); - this.ltr.put(node, related); - this.rtl.put(related, node); - final int count = node.getChildCount(); - for (int index = 0; index < count; index = index + 1) { - final Node first = node.getChild(index); - final Node second = related.getChild(index); - this.mapSubtreesWithTheSameHash(first, second, draft); - } - } - - /** - * Finds a partially mapped node from the 'left' set, that is, one that has some children - * mapped and some not. - * @return A node or {@code null} if such node not found - */ - private Node findPartiallyMappedLeftNode() { - Node result = null; - final Iterator iterator = this.left.iterator(); - while (result == null && iterator.hasNext()) { - final Node node = iterator.next(); - if (this.unprocessed.contains(node)) { - final int count = node.getChildCount(); - for (int index = 0; index < count; index = index + 1) { - final Node child = node.getChild(index); - if (this.ltr.containsKey(child)) { - result = node; - break; - } - } - } - } - return result; - } - - /** - * Tries to map a partially mapped 'left' node to another node. - * @param node A node to be mapped - * @return Next partially mapped node to be processed - */ - private Node mapPartiallyMappedLeftNode(final Node node) { - final Set ancestors = new HashSet<>(); - Node next = null; - node.forEachChild( - child -> { - final Node mapped = this.ltr.get(child); - if (mapped != null) { - ancestors.add(this.parents.get(mapped)); - } - } - ); - do { - if (ancestors.size() != 1) { - break; - } - final Node related = ancestors.iterator().next(); - if (!node.getTypeName().equals(related.getTypeName()) - || !node.getData().equals(related.getData())) { - break; - } - this.unprocessed.remove(related); - this.ltr.put(node, related); - this.rtl.put(related, node); - final boolean mapped = this.mapChildren(node, related); - assert mapped; - next = this.parents.get(node); - } while (false); - this.unprocessed.remove(node); - return next; - } - - /** - * Maps the child nodes of partially mapped nodes. - * @param before Node before changes - * @param after Node after changes - * @return Mapping result, {@code true} if at least one action has been added - */ - private boolean mapChildren(final Node before, final Node after) { - final int sign = Integer.compare(before.getChildCount(), after.getChildCount()); - final boolean result; - if (sign < 0) { - result = this.mapChildrenIfInserted(before, after); - } else if (sign > 0) { - result = this.mapChildrenIfDeleted(before); - } else { - this.mapChildrenIfReplaced(before, after); - result = true; - } - return result; - } - - /** - * Maps the child nodes of partially mapped nodes if the node before changes - * has less child nodes than the node after changes, i.e., when it is obvious - * that some nodes have been inserted. - * @param before Node before changes - * @param after Node after changes - * @return Mapping result, {@code true} if at least one action has been added - */ - private boolean mapChildrenIfInserted(final Node before, final Node after) { - final int count = after.getChildCount(); - boolean result = false; - Node previous = null; - for (int index = 0; index < count; index = index + 1) { - final Node child = after.getChild(index); - if (this.rtl.containsKey(child)) { - previous = this.rtl.get(child); - } else { - this.inserted.add(new Insertion(child, before, previous)); - this.unprocessed.remove(child); - result = true; - } - } - return result; - } - - /** - * Maps the child nodes of partially mapped nodes if the node before changes - * has the same number of child nodes as the node after changes, i.e., when it is obvious - * that some nodes have been replaced. - * @param before Node before changes - * @param after Node after changes - */ - private void mapChildrenIfReplaced(final Node before, final Node after) { - final int count = before.getChildCount(); - assert count == after.getChildCount(); - for (int index = 0; index < count; index = index + 1) { - final Node first = before.getChild(index); - if (!this.ltr.containsKey(first)) { - final Node second = after.getChild(index); - if (!this.mapTwoNodes(first, second)) { - this.replaced.put(first, second); - this.unprocessed.remove(first); - this.unprocessed.remove(second); - } - } - } - } - - /** - * Maps the child nodes of partially mapped nodes if the node before changes - * has more child nodes than the node after changes, i.e., when it is obvious - * that some nodes have been deleted. - * @param before Node before changes - * @return Mapping result, {@code true} if at least one action has been added - */ - private boolean mapChildrenIfDeleted(final Node before) { - final int count = before.getChildCount(); - boolean result = false; - for (int index = 0; index < count; index = index + 1) { - final Node child = before.getChild(index); - if (!this.ltr.containsKey(child)) { - this.deleted.add(child); - this.unprocessed.remove(child); - result = true; - } - } - return result; - } - - /** - * Trying to map the two nodes. - * @param before Node before changes - * @param after Node after changes - * @return Mapping result, {@code true} if nodes have been mapped - */ - private boolean mapTwoNodes(final Node before, final Node after) { - assert !this.ltr.containsKey(before); - boolean result = false; - if (before.getTypeName().equals(after.getTypeName()) - && before.getData().equals(after.getData())) { - this.unprocessed.remove(before); - this.unprocessed.remove(after); - this.ltr.put(before, after); - this.rtl.put(after, before); - final boolean mapped = this.mapChildren(before, after); - assert mapped; - result = true; - } - return result; - } - - /** - * Mapping result. - * - * @since 1.1.0 - */ - private static final class Result implements Mapping { - /** - * Structure from which the mapping results can be taken. - */ - private final BottomUpAlgorithm data; - - /** - * Constructor. - * @param data Structure from which the mapping results can be taken - */ - private Result(final BottomUpAlgorithm data) { - this.data = data; - } - - @Override - public Node getRight(final Node node) { - return this.data.ltr.get(node); - } - - @Override - public Node getLeft(final Node node) { - return this.data.rtl.get(node); - } - - @Override - public Set getInserted() { - return Collections.unmodifiableSet(this.data.inserted); - } - - @Override - public Map getReplaced() { - return Collections.unmodifiableMap(this.data.replaced); - } - - @Override - public Set getDeleted() { - return Collections.unmodifiableSet(this.data.deleted); - } - } -} diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapper.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapper.java deleted file mode 100644 index 806c4d1..0000000 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapper.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ivan Kniazkov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package org.cqfn.astranaut.core.algorithms.mapping; - -import org.cqfn.astranaut.core.Node; - -/** - * Bottom-up mapper. - * Tries to match leaf nodes first, and then subtrees containing leaf nodes, - * gradually increasing the size of the matched subtrees. - * - * @since 1.1.0 - */ -public final class BottomUpMapper implements Mapper { - @Override - public Mapping map(final Node left, final Node right) { - final BottomUpAlgorithm algorithm = new BottomUpAlgorithm(left, right); - algorithm.execute(); - return algorithm.getResult(); - } -} diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/DraftMapping.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/DraftMapping.java deleted file mode 100644 index 8ace7f1..0000000 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/DraftMapping.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ivan Kniazkov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package org.cqfn.astranaut.core.algorithms.mapping; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import org.cqfn.astranaut.core.Node; - -/** - * A mapping that has ambiguities, that is, one-to-many relations, - * that is, that offers multiple variants of 'right' tree nodes that are somehow related - * to a single 'left' tree node. - * - * @since 1.1.3 - */ -final class DraftMapping { - /** - * Left-to-right relations. - */ - private final Map> ltr; - - /** - * Right-to-left relations. - */ - private final Map> rtl; - - /** - * Constructor. - */ - DraftMapping() { - this.ltr = new HashMap<>(); - this.rtl = new HashMap<>(); - } - - /** - * Adds the relation of a node from the 'left' tree to some set of nodes from the 'right' tree. - * @param left Node from the 'left' tree - * @param right Related nodes from the 'right' tree - */ - public void addRelation(final Node left, final Set right) { - this.ltr.put(left, right); - for (final Node node : right) { - final Set set = this.rtl.computeIfAbsent(node, k -> new HashSet<>()); - set.add(left); - } - } - - /** - * Returns the relation of a node from the 'left' tree to some set of nodes. - * from the 'right' tree - * @param left Node from the 'left' tree - * @return Related nodes from the 'right' tree or empty set - */ - public Set getRelation(final Node left) { - return this.ltr.getOrDefault(left, Collections.emptySet()); - } - - /** - * Removes the relationship of nodes from the 'left' and 'right' trees. - * This reduces ambiguity, i.e. if a 'left' node has been correlated with a 'right' node, - * it can no longer be correlated with any other 'right' node. - * There should be no ambiguities remaining at the end of the mapping. - * @param left Node from the 'left' tree - * @param right Node from the 'right' tree - */ - public void removeRelation(final Node left, final Node right) { - this.ltr.remove(left); - if (this.rtl.containsKey(right)) { - for (final Node node : this.rtl.get(right)) { - final Set set = this.ltr.get(node); - if (set != null) { - set.remove(right); - } - } - } - } - - /** - * Returns set of mapped nodes from the 'left' tree. - * @return Set of nodes that have relations - */ - public Set getLeftNodes() { - return this.ltr.keySet(); - } - - /** - * Returns {@code} if this collection contains no mappings. - * @return Checking result - */ - public boolean isEmpty() { - return this.ltr.isEmpty(); - } -} diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java index ed08a84..b1aea84 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java @@ -27,7 +27,7 @@ import org.cqfn.astranaut.core.Node; import org.cqfn.astranaut.core.algorithms.hash.AbsoluteHash; import org.cqfn.astranaut.core.algorithms.hash.Hash; -import org.cqfn.astranaut.core.algorithms.mapping.BottomUpMapper; +import org.cqfn.astranaut.core.algorithms.mapping.TopDownMapper; import org.cqfn.astranaut.core.example.LittleTrees; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; @@ -49,7 +49,7 @@ void testTreeWithInsertedNode() { LittleTrees.createIntegerLiteral(3) ); final DifferenceTreeBuilder builder = new DifferenceTreeBuilder(before); - final boolean result = builder.build(after, new BottomUpMapper()); + final boolean result = builder.build(after, TopDownMapper.INSTANCE); Assertions.assertTrue(result); final DifferenceNode diff = builder.getRoot(); final Node expected = LittleTrees.createTreeWithInsertAction(); @@ -70,7 +70,7 @@ void testTreeWithReplacedNode() { LittleTrees.createVariable("x") ); final DifferenceTreeBuilder builder = new DifferenceTreeBuilder(before); - final boolean result = builder.build(after, new BottomUpMapper()); + final boolean result = builder.build(after, TopDownMapper.INSTANCE); Assertions.assertTrue(result); final DifferenceNode diff = builder.getRoot(); final Node expected = LittleTrees.createTreeWithReplaceAction(); @@ -93,7 +93,7 @@ void testTreeWithDeletedNode() { ); final Node after = LittleTrees.createStatementListWithTwoChildren(); final DifferenceTreeBuilder builder = new DifferenceTreeBuilder(before); - final boolean result = builder.build(after, new BottomUpMapper()); + final boolean result = builder.build(after, TopDownMapper.INSTANCE); Assertions.assertTrue(result); final DifferenceNode diff = builder.getRoot(); final Node expected = LittleTrees.createTreeWithDeleteAction(); @@ -118,7 +118,7 @@ void testTreeWithDeletedNodeInDepth() { LittleTrees.createStatementListWithTwoChildren() ); final DifferenceTreeBuilder builder = new DifferenceTreeBuilder(before); - final boolean result = builder.build(after, new BottomUpMapper()); + final boolean result = builder.build(after, TopDownMapper.INSTANCE); Assertions.assertTrue(result); final DifferenceNode diff = builder.getRoot(); final Node expected = LittleTrees.createTreeWithDeleteActionInDepth(); @@ -178,7 +178,7 @@ void testTreeWithReplacedNotUniqueNode() { Assertions.assertTrue(before.deepCompare(expected.getBefore())); Assertions.assertTrue(after.deepCompare(expected.getAfter())); final DifferenceTreeBuilder second = new DifferenceTreeBuilder(before); - second.build(after, new BottomUpMapper()); + second.build(after, TopDownMapper.INSTANCE); final DifferenceNode actual = second.getRoot(); Assertions.assertTrue(before.deepCompare(actual.getBefore())); Assertions.assertTrue(after.deepCompare(actual.getAfter())); diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapperTest.java deleted file mode 100644 index dd6d2e0..0000000 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/BottomUpMapperTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright (c) 2024 Ivan Kniazkov - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package org.cqfn.astranaut.core.algorithms.mapping; - -import java.util.Set; -import org.cqfn.astranaut.core.Node; -import org.cqfn.astranaut.core.example.LittleTrees; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -/** - * Test for {@link BottomUpMapper} class. - * - * @since 1.0 - */ -class BottomUpMapperTest { - /** - * Test in which the identical trees are mapped. - */ - @Test - void testIdenticalTrees() { - final Node first = LittleTrees.createTreeWithDeleteAction(); - final Node second = LittleTrees.createTreeWithDeleteAction(); - final Mapper mapper = new BottomUpMapper(); - final Mapping mapping = mapper.map(first, second); - Assertions.assertEquals(mapping.getRight(first), second); - Assertions.assertEquals(mapping.getLeft(second), first); - } - - /** - * Testing mapping of two trees, with some node removed in the second tree. - */ - @Test - void testOneWasRemoved() { - final Node first = LittleTrees.createStatementListWithThreeChildren( - LittleTrees.createIntegerLiteral(2) - ); - final Node second = LittleTrees.createStatementListWithTwoChildren(); - final Mapper mapper = new BottomUpMapper(); - final Mapping mapping = mapper.map(first, second); - Assertions.assertEquals(mapping.getRight(first), second); - Assertions.assertEquals(mapping.getLeft(second), first); - Node left = first.getChild(0); - Node right = second.getChild(0); - Assertions.assertEquals(mapping.getRight(left), right); - Assertions.assertEquals(mapping.getLeft(right), left); - left = first.getChild(2); - right = second.getChild(1); - Assertions.assertEquals(mapping.getRight(left), right); - Assertions.assertEquals(mapping.getLeft(right), left); - final Set deleted = mapping.getDeleted(); - Assertions.assertEquals(1, deleted.size()); - Assertions.assertEquals(first.getChild(1), deleted.iterator().next()); - } -} From 5ae3a1baf3effb6183a9f454acb9405172836758 Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Mon, 1 Apr 2024 12:54:13 +0300 Subject: [PATCH 08/14] Top-down mapper: implement mapper for the case where some nodes are inserted and some are replaced --- .../algorithms/mapping/TopDownAlgorithm.java | 101 +++++++++++++++--- .../algorithms/mapping/TopDownMapperTest.java | 16 +++ 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index 5249080..7a4f68b 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -169,12 +169,27 @@ private void mapSubtreesWithDifferentHashes(final Node left, final Node right) { */ private boolean mapTwoFirstUnmappedNodes(final Node left, final Node right, final Unprocessed unprocessed) { - final Node first = this.findFirstUnmappedChild(left); - final Node second = this.findFirstUnmappedChild(right); - final boolean result = this.execute(first, second); - if (result) { - unprocessed.removeOnePair(); - } + final Child first = this.findFirstUnmappedChild(left); + final Child second = this.findFirstUnmappedChild(right); + boolean result; + do { + result = this.execute(first.node, second.node); + if (result) { + unprocessed.removeOnePair(); + break; + } + if (second.after != null) { + result = this.execute(first.node, second.after); + } + if (result) { + unprocessed.removeOnePair(); + final Insertion insertion = new Insertion(second.node, right, first.before); + this.inserted.add(insertion); + this.rtl.put(second.node, null); + unprocessed.nodeWasInserted(); + break; + } + } while (false); return result; } @@ -191,11 +206,11 @@ private boolean mapTwoFirstUnmappedNodes(final Node left, final Node right, */ private void replaceTwoFirstUnmappedNodes(final Node left, final Node right, final Unprocessed unprocessed) { - final Node first = this.findFirstUnmappedChild(left); - final Node second = this.findFirstUnmappedChild(right); + final Node first = this.findFirstUnmappedChild(left).node; + final Node second = this.findFirstUnmappedChild(right).node; this.replaced.put(first, second); - this.ltr.put(first, null); - this.rtl.put(second, null); + this.ltr.put(first, second); + this.rtl.put(second, first); unprocessed.removeOnePair(); } @@ -204,13 +219,21 @@ private void replaceTwoFirstUnmappedNodes(final Node left, final Node right, * @param node Parent node * @return First child node that has not yet been mapped */ - private Node findFirstUnmappedChild(final Node node) { + private Child findFirstUnmappedChild(final Node node) { final int count = node.getChildCount(); - Node result = null; + Child result = null; for (int index = 0; index < count; index = index + 1) { final Node child = node.getChild(index); if (!this.ltr.containsKey(child) && !this.rtl.containsKey(child)) { - result = child; + Node before = null; + if (index > 0) { + before = node.getChild(index - 1); + } + Node after = null; + if (index < count - 1) { + after = node.getChild(index + 1); + } + result = new Child(child, before, after); break; } } @@ -357,7 +380,7 @@ private static class Unprocessed { /** * Number of nodes to be added. */ - private final int add; + private int add; /** * Number of nodes to be deleted. @@ -400,6 +423,16 @@ boolean onlyActionIsToDeleteNodes() { return this.right == 0 && this.delete == this.left; } + /** + * Notes that some child node of the right node has been recognized as an inserted node. + */ + void nodeWasInserted() { + assert this.right > 0; + this.right = this.right - 1; + assert this.add > 0; + this.add = this.add - 1; + } + /** * Marks that some child of the left node has been mapped or replaced by a child * of the right node. @@ -411,4 +444,44 @@ void removeOnePair() { assert this.left >= this.delete; } } + + /** + * A child node found by some criteria. + * + * @since 1.1.0 + */ + private static class Child { + /** + * Child node itself. + */ + private final Node node; + + /** + * Child node before (if exists). + */ + private final Node before; + + /** + * Child node after (if exists). + */ + private final Node after; + + /** + * Constructor. + * @param node Child node itself + * @param before Child node before + * @param after Child node after + */ + Child(final Node node, final Node before, final Node after) { + this.node = node; + this.before = before; + this.after = after; + } + + @Override + public String toString() { + return this.node.toString(); + } + } } + diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 9deffa6..0c3315a 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -124,4 +124,20 @@ void testPairOfTreesWhereThreeAndOneReplaced() { Assertions.assertEquals("B", pair.getKey().getTypeName()); Assertions.assertEquals("D", pair.getValue().getTypeName()); } + + @Test + void testPairOfTreesWhereOneAddedAndOneReplaced() { + final Node first = DraftNode.createByDescription("X(A,Y(C,D,E))"); + final Node second = DraftNode.createByDescription("X(A,Y(B,C,F,E))"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set added = mapping.getInserted(); + Assertions.assertEquals(1, added.size()); + Assertions.assertEquals("B", added.iterator().next().getNode().getTypeName()); + final Map replaced = mapping.getReplaced(); + Assertions.assertEquals(1, replaced.size()); + final Map.Entry pair = replaced.entrySet().iterator().next(); + Assertions.assertEquals("D", pair.getKey().getTypeName()); + Assertions.assertEquals("F", pair.getValue().getTypeName()); + } } From 3de1c1667e64c41ecf2bc7d2c29fe61e282402af Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Mon, 1 Apr 2024 13:06:07 +0300 Subject: [PATCH 09/14] + test --- .../algorithms/mapping/TopDownAlgorithm.java | 22 ++++++++++++++++++- .../algorithms/mapping/TopDownMapperTest.java | 16 ++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index 7a4f68b..c9f73a3 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -189,6 +189,16 @@ private boolean mapTwoFirstUnmappedNodes(final Node left, final Node right, unprocessed.nodeWasInserted(); break; } + if (first.after != null) { + result = this.execute(first.after, second.node); + } + if (result) { + unprocessed.removeOnePair(); + this.deleted.add(first.node); + this.ltr.put(first.node, null); + unprocessed.nodeWasDeleted(); + break; + } } while (false); return result; } @@ -385,7 +395,7 @@ private static class Unprocessed { /** * Number of nodes to be deleted. */ - private final int delete; + private int delete; /** * Constructor. @@ -433,6 +443,16 @@ void nodeWasInserted() { this.add = this.add - 1; } + /** + * Notes that some child node of the right node has been recognized as a deleted node. + */ + void nodeWasDeleted() { + assert this.left > 0; + this.left = this.left - 1; + assert this.delete > 0; + this.delete = this.delete - 1; + } + /** * Marks that some child of the left node has been mapped or replaced by a child * of the right node. diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 0c3315a..11255b1 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -140,4 +140,20 @@ void testPairOfTreesWhereOneAddedAndOneReplaced() { Assertions.assertEquals("D", pair.getKey().getTypeName()); Assertions.assertEquals("F", pair.getValue().getTypeName()); } + + @Test + void testPairOfTreesWhereOneDeletedAndOneReplaced() { + final Node first = DraftNode.createByDescription("X(A,Y(B,C,D,E))"); + final Node second = DraftNode.createByDescription("X(A,Y(C,F,E))"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set deleted = mapping.getDeleted(); + Assertions.assertEquals(1, deleted.size()); + Assertions.assertEquals("B", deleted.iterator().next().getTypeName()); + final Map replaced = mapping.getReplaced(); + Assertions.assertEquals(1, replaced.size()); + final Map.Entry pair = replaced.entrySet().iterator().next(); + Assertions.assertEquals("D", pair.getKey().getTypeName()); + Assertions.assertEquals("F", pair.getValue().getTypeName()); + } } From 203568b77d6fda08806ac6d09702123280785010 Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Tue, 2 Apr 2024 08:38:08 +0300 Subject: [PATCH 10/14] + test --- .../algorithms/mapping/TopDownMapperTest.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 11255b1..408f87a 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -156,4 +156,22 @@ void testPairOfTreesWhereOneDeletedAndOneReplaced() { Assertions.assertEquals("D", pair.getKey().getTypeName()); Assertions.assertEquals("F", pair.getValue().getTypeName()); } + + @Test + void testPairOfTreesWhereTwoAddedAndOneReplaced() { + final Node first = DraftNode.createByDescription("X(A,Y(B,C,D))"); + final Node second = DraftNode.createByDescription("X(A,Y(B,E,D,F,F))"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set insertions = mapping.getInserted(); + Assertions.assertEquals(2, insertions.size()); + for (final Insertion insertion : insertions) { + Assertions.assertEquals("F", insertion.getNode().getTypeName()); + } + final Map replaced = mapping.getReplaced(); + Assertions.assertEquals(1, replaced.size()); + final Map.Entry pair = replaced.entrySet().iterator().next(); + Assertions.assertEquals("C", pair.getKey().getTypeName()); + Assertions.assertEquals("E", pair.getValue().getTypeName()); + } } From acacb1e99025fb92101d09a38796a7cf9956340a Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Tue, 2 Apr 2024 08:45:32 +0300 Subject: [PATCH 11/14] + test --- .../algorithms/mapping/TopDownMapperTest.java | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 408f87a..6e1f6e9 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -160,7 +160,7 @@ void testPairOfTreesWhereOneDeletedAndOneReplaced() { @Test void testPairOfTreesWhereTwoAddedAndOneReplaced() { final Node first = DraftNode.createByDescription("X(A,Y(B,C,D))"); - final Node second = DraftNode.createByDescription("X(A,Y(B,E,D,F,F))"); + final Node second = DraftNode.createByDescription("X(A,Y(B,E,F,D,F))"); final Mapper mapper = TopDownMapper.INSTANCE; final Mapping mapping = mapper.map(first, second); final Set insertions = mapping.getInserted(); @@ -174,4 +174,22 @@ void testPairOfTreesWhereTwoAddedAndOneReplaced() { Assertions.assertEquals("C", pair.getKey().getTypeName()); Assertions.assertEquals("E", pair.getValue().getTypeName()); } + + @Test + void testPairOfTreesWhereTwoRemovedAndOneReplaced() { + final Node first = DraftNode.createByDescription("X(A,Y(B,E,F,D,F))"); + final Node second = DraftNode.createByDescription("X(A,Y(B,C,D))"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set deletions = mapping.getDeleted(); + Assertions.assertEquals(2, deletions.size()); + for (final Node deleted : deletions) { + Assertions.assertEquals("F", deleted.getTypeName()); + } + final Map replaced = mapping.getReplaced(); + Assertions.assertEquals(1, replaced.size()); + final Map.Entry pair = replaced.entrySet().iterator().next(); + Assertions.assertEquals("E", pair.getKey().getTypeName()); + Assertions.assertEquals("C", pair.getValue().getTypeName()); + } } From 7695e4a544ef29d378ea60bdbc032e07b514156a Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Tue, 2 Apr 2024 09:21:14 +0300 Subject: [PATCH 12/14] + test --- .../algorithms/mapping/TopDownAlgorithm.java | 14 +++++++--- .../algorithms/mapping/TopDownMapperTest.java | 28 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index c9f73a3..5fd1a47 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -439,8 +439,11 @@ boolean onlyActionIsToDeleteNodes() { void nodeWasInserted() { assert this.right > 0; this.right = this.right - 1; - assert this.add > 0; - this.add = this.add - 1; + if (this.add > 0) { + this.add = this.add - 1; + } else { + this.delete = this.delete + 1; + } } /** @@ -449,8 +452,11 @@ void nodeWasInserted() { void nodeWasDeleted() { assert this.left > 0; this.left = this.left - 1; - assert this.delete > 0; - this.delete = this.delete - 1; + if (this.delete > 0) { + this.delete = this.delete - 1; + } else { + this.add = this.add + 1; + } } /** diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 6e1f6e9..4bb9062 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import org.cqfn.astranaut.core.DraftNode; import org.cqfn.astranaut.core.Insertion; import org.cqfn.astranaut.core.Node; @@ -192,4 +193,31 @@ void testPairOfTreesWhereTwoRemovedAndOneReplaced() { Assertions.assertEquals("E", pair.getKey().getTypeName()); Assertions.assertEquals("C", pair.getValue().getTypeName()); } + + @Test + void testPairOfTreesWhereAllActions() { + final Node first = DraftNode.createByDescription("X(A,B,Y(C,D,E,F,J,K))"); + final Node second = DraftNode.createByDescription("X(A,G,Y(H,C,I,E,J,K))"); + final Mapper mapper = TopDownMapper.INSTANCE; + final Mapping mapping = mapper.map(first, second); + final Set inserted = mapping.getInserted(); + Assertions.assertEquals(1, inserted.size()); + Assertions.assertEquals("H", inserted.iterator().next().getNode().getTypeName()); + final Set deleted = mapping.getDeleted(); + Assertions.assertEquals(1, deleted.size()); + Assertions.assertEquals("F", deleted.iterator().next().getTypeName()); + final Map replaced = mapping.getReplaced(); + Assertions.assertEquals(2, replaced.size()); + final Map expected = new TreeMap(){{ + put("B", "G"); + put("D", "I"); + }}; + for (Map.Entry pair : replaced.entrySet()) { + Assertions.assertTrue(expected.containsKey(pair.getKey().getTypeName())); + Assertions.assertEquals( + pair.getValue().getTypeName(), + expected.get(pair.getKey().getTypeName()) + ); + } + } } From bcad2a4c908440efd0240f5d7b8bee36eff6649e Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Tue, 2 Apr 2024 09:54:38 +0300 Subject: [PATCH 13/14] Enable old test, added another test --- .../core/algorithms/mapping/TopDownAlgorithm.java | 11 ++++++----- .../algorithms/DifferenceTreeBuilderTest.java | 15 +++++++++++++-- .../algorithms/mapping/TopDownMapperTest.java | 10 +++++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java index 5fd1a47..4b8b8d8 100644 --- a/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java +++ b/src/main/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownAlgorithm.java @@ -142,7 +142,7 @@ private void mapSubtreesWithDifferentHashes(final Node left, final Node right) { assert unprocessed.hasNodes(); do { if (unprocessed.onlyActionIsToInsertNodes()) { - this.insertAllNotYetMappedNodes(right); + this.insertAllNotYetMappedNodes(left, right); break; } if (unprocessed.onlyActionIsToDeleteNodes()) { @@ -183,7 +183,7 @@ private boolean mapTwoFirstUnmappedNodes(final Node left, final Node right, } if (result) { unprocessed.removeOnePair(); - final Insertion insertion = new Insertion(second.node, right, first.before); + final Insertion insertion = new Insertion(second.node, left, first.before); this.inserted.add(insertion); this.rtl.put(second.node, null); unprocessed.nodeWasInserted(); @@ -292,9 +292,10 @@ private Node findLastUnmappedChild(final Node node) { /** * For all child nodes of the right node that are not yet mapped, performs * the 'Insert' operation. + * @param left Left node * @param right Related node to the left node */ - private void insertAllNotYetMappedNodes(final Node right) { + private void insertAllNotYetMappedNodes(final Node left, final Node right) { final int count = right.getChildCount(); Node after = null; for (int index = 0; index < count; index = index + 1) { @@ -302,7 +303,7 @@ private void insertAllNotYetMappedNodes(final Node right) { if (this.rtl.containsKey(node)) { after = this.rtl.get(node); } else { - final Insertion insertion = new Insertion(node, right, after); + final Insertion insertion = new Insertion(node, left, after); this.inserted.add(insertion); this.rtl.put(node, null); after = node; @@ -313,7 +314,7 @@ private void insertAllNotYetMappedNodes(final Node right) { /** * For all child nodes of the left node that are not yet mapped, performs * the 'Delete' operation. - * @param left Related node to the left node + * @param left Left node */ private void deleteAllNotYetMappedNodes(final Node left) { final int count = left.getChildCount(); diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java index b1aea84..a5be0bf 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/DifferenceTreeBuilderTest.java @@ -24,13 +24,13 @@ package org.cqfn.astranaut.core.algorithms; import org.cqfn.astranaut.core.DifferenceNode; +import org.cqfn.astranaut.core.DraftNode; import org.cqfn.astranaut.core.Node; import org.cqfn.astranaut.core.algorithms.hash.AbsoluteHash; import org.cqfn.astranaut.core.algorithms.hash.Hash; import org.cqfn.astranaut.core.algorithms.mapping.TopDownMapper; import org.cqfn.astranaut.core.example.LittleTrees; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** @@ -128,7 +128,6 @@ void testTreeWithDeletedNodeInDepth() { } @Test - @Disabled void testTreeWithReplacedNotUniqueNode() { final Node removed = LittleTrees.createIntegerLiteral(1); final Node added = LittleTrees.createIntegerLiteral(2); @@ -184,4 +183,16 @@ void testTreeWithReplacedNotUniqueNode() { Assertions.assertTrue(after.deepCompare(actual.getAfter())); Assertions.assertTrue(actual.deepCompare(expected)); } + + @Test + void testComplexCase() { + final Node before = DraftNode.createByDescription("X(A,B,Y(C,D,E,F,J,K))"); + final Node after = DraftNode.createByDescription("X(A,G,Y(H,C,I,E,J,K))"); + final DifferenceTreeBuilder builder = new DifferenceTreeBuilder(before); + final boolean result = builder.build(after, TopDownMapper.INSTANCE); + Assertions.assertTrue(result); + final DifferenceNode diff = builder.getRoot(); + Assertions.assertTrue(before.deepCompare(diff.getBefore())); + Assertions.assertTrue(after.deepCompare(diff.getAfter())); + } } diff --git a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java index 4bb9062..8cafae7 100644 --- a/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java +++ b/src/test/java/org/cqfn/astranaut/core/algorithms/mapping/TopDownMapperTest.java @@ -39,6 +39,7 @@ * * @since 1.0 */ +@SuppressWarnings("PMD.TooManyMethods") class TopDownMapperTest { @Test void testIdenticalTrees() { @@ -208,11 +209,10 @@ void testPairOfTreesWhereAllActions() { Assertions.assertEquals("F", deleted.iterator().next().getTypeName()); final Map replaced = mapping.getReplaced(); Assertions.assertEquals(2, replaced.size()); - final Map expected = new TreeMap(){{ - put("B", "G"); - put("D", "I"); - }}; - for (Map.Entry pair : replaced.entrySet()) { + final Map expected = new TreeMap<>(); + expected.put("B", "G"); + expected.put("D", "I"); + for (final Map.Entry pair : replaced.entrySet()) { Assertions.assertTrue(expected.containsKey(pair.getKey().getTypeName())); Assertions.assertEquals( pair.getValue().getTypeName(), From 6183ac28ef477f9ccb682155fe4983872baf84e9 Mon Sep 17 00:00:00 2001 From: Ivan Kniazkov Date: Tue, 2 Apr 2024 09:58:05 +0300 Subject: [PATCH 14/14] Remove old test --- .../core/utils/TreeVisualizerTest.java | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/test/java/org/cqfn/astranaut/core/utils/TreeVisualizerTest.java b/src/test/java/org/cqfn/astranaut/core/utils/TreeVisualizerTest.java index 65d1841..c20a56b 100644 --- a/src/test/java/org/cqfn/astranaut/core/utils/TreeVisualizerTest.java +++ b/src/test/java/org/cqfn/astranaut/core/utils/TreeVisualizerTest.java @@ -30,10 +30,8 @@ import org.cqfn.astranaut.core.DraftNode; import org.cqfn.astranaut.core.EmptyTree; import org.cqfn.astranaut.core.Node; -import org.cqfn.astranaut.core.example.LittleTrees; import org.cqfn.astranaut.core.exceptions.WrongFileExtension; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -149,21 +147,4 @@ void testWrongExtension(@TempDir final Path temp) { } Assertions.assertTrue(oops); } - - /** - * Test for a tree visualization. - */ - @Test - @Disabled - void testActions() { - final TreeVisualizer visualizer = - new TreeVisualizer(LittleTrees.createTreeWithDeleteAction()); - boolean oops = false; - try { - visualizer.visualize(new File("X:\\syntax_tree_with_action.png")); - } catch (final WrongFileExtension | IOException exception) { - oops = true; - } - Assertions.assertFalse(oops); - } }