Skip to content

Commit

Permalink
Merge pull request #20 from GingerYouth/mos-1022
Browse files Browse the repository at this point in the history
First version of identical nodes finding implementation with some tests.
  • Loading branch information
kniazkov authored Apr 10, 2024
2 parents 9b9b2c5 + 444ad09 commit 9b75009
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 0 deletions.
97 changes: 97 additions & 0 deletions src/main/java/org/cqfn/astranaut/core/algorithms/Identical.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.cqfn.astranaut.core.Node;
import org.cqfn.astranaut.core.algorithms.hash.SimpleHash;

/**
* A class which finds and returns identical nodes in a tree.
* The nodes are identical if their data is not empty, and their {@link SimpleHash} equals.
*
* @since 1.1.5
*/
@SuppressWarnings("PMD.ConstructorShouldDoInitialization")
public class Identical {
/**
* The root of the tree to search in.
*/
private final Node root;

/**
* The {@link SimpleHash} storage for the tree nodes.
*/
private final SimpleHash hashes;

/**
* The map of the calculated hashes and corresponding nodes.
*/
private final Map<Integer, Set<Node>> results = new HashMap<>();

/**
* Constructor.
*
* @param root The root of the tree to search in
*/
public Identical(final Node root) {
this.root = root;
this.hashes = new SimpleHash();
}

/**
* Get identical nodes.
*
* @return The set of sets with identical nodes.
*/
public Set<Set<Node>> get() {
this.search(this.root);
return this.results.values()
.stream()
.filter(set -> set.size() >= 2)
.collect(Collectors.toSet());
}

/**
* Calculates {@link SimpleHash} recursively for all nodes with non-empty data and
* adds entries to the resulting map.
*
* @param node The current node to process
*/
private void search(final Node node) {
if (!node.getData().isEmpty()) {
this.results.computeIfAbsent(
this.hashes.calculate(node),
s -> new HashSet<>()
).add(node);
}
for (final Node child: node.getChildrenList()) {
this.search(child);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.hash;

import java.util.HashMap;
import java.util.Map;
import org.cqfn.astranaut.core.Node;

/**
* Computes the simple hash of a node, that is, a hash that is computed from the type
* and the data of the node itself, regardless of the children of the node.
*
* @since 1.1.5
*/
public final class SimpleHash implements Hash {
/**
* A table with the calculated hashes.
* Since nodes are immutable, a hash calculated once for a node will never change.
*/
private final Map<Node, Integer> calculated;

/**
* Constructor.
*/
public SimpleHash() {
this.calculated = new HashMap<>();
}

@Override
public int calculate(final Node node) {
final int hash;
if (this.calculated.containsKey(node)) {
hash = this.calculated.get(node);
} else {
hash = node.getTypeName().hashCode() * 31 + node.getData().hashCode();
this.calculated.put(node, hash);
}
return hash;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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;

import java.util.Set;
import org.cqfn.astranaut.core.DraftNode;
import org.cqfn.astranaut.core.Node;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
* Tests for {@link Identical} class.
*
* @since 1.1.5
*/
class IdenticalTest {
@Test
void testIdenticalSets() {
final Node original = DraftNode.createByDescription(
"T<\"a\">(T<\"b\">,T<\"c\">(F<\"a\">,T<\"b\">,T<\"a\">,F<\"a\">))"
);
final Identical identical = new Identical(original);
final Set<Set<Node>> identicals = identical.get();
Assertions.assertEquals(3, identicals.size());
}

@Test
void testNoIdenticals() {
final Node original = DraftNode.createByDescription(
"T<\"a\">(T<\"b\">,T<\"c\">(F<\"a\">,F<\"b\">,F<\"c\">,K<\"a\">))"
);
final Identical identical = new Identical(original);
final Set<Set<Node>> identicals = identical.get();
Assertions.assertEquals(0, identicals.size());
}

@Test
void testIdenticalsAndEmptyData() {
final Node original = DraftNode.createByDescription(
"T<\"a\">(T<\"b\">,T<\"c\">(F<\"a\">,T<\"b\">,T<\"a\">,F,F,T))"
);
final Identical identical = new Identical(original);
final Set<Set<Node>> identicals = identical.get();
Assertions.assertEquals(2, identicals.size());
}
}

0 comments on commit 9b75009

Please sign in to comment.