-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from GingerYouth/mos-1005
First implementation of the subtrees
- Loading branch information
Showing
5 changed files
with
514 additions
and
4 deletions.
There are no files selected for viewing
130 changes: 130 additions & 0 deletions
130
src/main/java/org/cqfn/astranaut/core/algorithms/NodeSelector.java
This file contains 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,130 @@ | ||
/* | ||
* 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.Deque; | ||
import java.util.HashSet; | ||
import java.util.LinkedList; | ||
import java.util.Set; | ||
import org.cqfn.astranaut.core.Node; | ||
|
||
/** | ||
* An algorithm that selects nodes from a tree based on some criteria. | ||
* | ||
* @since 1.1.4 | ||
*/ | ||
public class NodeSelector { | ||
/** | ||
* The root node of the tree from which the nodes will be selected. | ||
*/ | ||
private final Node root; | ||
|
||
/** | ||
* Constructor. | ||
* @param root The root node of the tree from which the nodes will be selected | ||
*/ | ||
public NodeSelector(final Node root) { | ||
this.root = root; | ||
} | ||
|
||
/** | ||
* Selects nodes from the tree based on some criterion. | ||
* @param criteria Node selection criteria | ||
* @return Set containing selected nodes | ||
*/ | ||
public Set<Node> select(final Criteria criteria) { | ||
final Walker walker = new Walker(criteria); | ||
walker.walk(this.root); | ||
return walker.set; | ||
} | ||
|
||
/** | ||
* Node selection criteria. | ||
* | ||
* @since 1.1.4 | ||
*/ | ||
public interface Criteria { | ||
/** | ||
* Checks if the node satisfies the criterion. | ||
* @param node Node | ||
* @param parents Parents of the node, starting from the immediate one (in case | ||
* the criterion analysis requires information about the parents) | ||
* @return Checking result | ||
*/ | ||
boolean isApplicable(Node node, Iterable<Node> parents); | ||
} | ||
|
||
/** | ||
* Walker that traverses the syntax tree and selects nodes. | ||
* | ||
* @since 1.1.4 | ||
*/ | ||
private static final class Walker { | ||
/** | ||
* Node selection criteria. | ||
*/ | ||
private final Criteria criteria; | ||
|
||
/** | ||
* Set containing selected nodes. | ||
*/ | ||
private final Set<Node> set; | ||
|
||
/** | ||
* Constructor. | ||
* @param criteria Node selection criteria | ||
*/ | ||
private Walker(final Criteria criteria) { | ||
this.criteria = criteria; | ||
this.set = new HashSet<>(); | ||
} | ||
|
||
/** | ||
* Starts the tree traversal. | ||
* @param root Root node of the tree | ||
*/ | ||
void walk(final Node root) { | ||
this.check(root, new LinkedList<>()); | ||
} | ||
|
||
/** | ||
* Checks the node and recursively all children of the node against the criterion. | ||
* @param node The node | ||
* @param parents Stack containing the parents of the node | ||
*/ | ||
void check(final Node node, final Deque<Node> parents) { | ||
if (this.criteria.isApplicable(node, parents)) { | ||
this.set.add(node); | ||
} | ||
final int count = node.getChildCount(); | ||
if (count > 0) { | ||
parents.addFirst(node); | ||
for (int index = 0; index < count; index = index + 1) { | ||
this.check(node.getChild(index), parents); | ||
} | ||
parents.removeFirst(); | ||
} | ||
} | ||
} | ||
} |
192 changes: 192 additions & 0 deletions
192
src/main/java/org/cqfn/astranaut/core/algorithms/Subtree.java
This file contains 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,192 @@ | ||
/* | ||
* 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.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import org.cqfn.astranaut.core.EmptyTree; | ||
import org.cqfn.astranaut.core.Fragment; | ||
import org.cqfn.astranaut.core.Node; | ||
import org.cqfn.astranaut.core.Type; | ||
|
||
/** | ||
* Algorithm that produces a subtree from the original tree. | ||
* | ||
* @since 1.1.4 | ||
*/ | ||
public class Subtree { | ||
/** | ||
* Algorithm that composes a subtree only from the nodes that are specified in the set. | ||
*/ | ||
public static final Algorithm INCLUDE = (node, set) -> set.contains(node); | ||
|
||
/** | ||
* Algorithm that composes a subtree from all nodes in the original tree, | ||
* but excludes nodes specified in the set. | ||
*/ | ||
public static final Algorithm EXCLUDE = (node, set) -> !set.contains(node); | ||
|
||
/** | ||
* The root node of the original tree. | ||
*/ | ||
private final Node root; | ||
|
||
/** | ||
* Algorithm that selects nodes based on some criteria. | ||
*/ | ||
private final Algorithm algorithm; | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param root The root node of the original tree | ||
* @param algorithm Algorithm that selects nodes based on some criteria | ||
*/ | ||
public Subtree(final Node root, final Algorithm algorithm) { | ||
this.root = root; | ||
this.algorithm = algorithm; | ||
} | ||
|
||
/** | ||
* Creates a subtree from the original tree, with the result depends on the nodes that are | ||
* specified in the set. | ||
* @param nodes The set of nodes | ||
* @return Root node of created subtree. | ||
*/ | ||
public Node create(final Set<Node> nodes) { | ||
final Map<Node, List<Integer>> indexes = new HashMap<>(); | ||
this.build(this.root, indexes, nodes); | ||
final Node result; | ||
if (indexes.get(this.root).isEmpty()) { | ||
result = EmptyTree.INSTANCE; | ||
} else { | ||
result = new SubNode(this.root, indexes); | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* Constructs an index map containing the indexes of the nodes that will be included | ||
* in the resulting tree. | ||
* @param node Current node | ||
* @param indexes Index map | ||
* @param set The set of nodes | ||
*/ | ||
private void build(final Node node, final Map<Node, List<Integer>> indexes, | ||
final Set<Node> set) { | ||
final List<Integer> list = indexes.computeIfAbsent(node, s -> new ArrayList<>(0)); | ||
final int count = node.getChildCount(); | ||
for (int index = 0; index < count; index = index + 1) { | ||
final Node child = node.getChild(index); | ||
if (this.algorithm.isApplicable(child, set)) { | ||
list.add(index); | ||
this.build(child, indexes, set); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* An algorithm that selects nodes based on some criteria. | ||
* | ||
* @since 1.1.4 | ||
*/ | ||
public interface Algorithm { | ||
/** | ||
* Checks if the node is applicable to the set. | ||
* @param node Node to be checked | ||
* @param set Set of nodes | ||
* @return Checking result. | ||
*/ | ||
boolean isApplicable(Node node, Set<Node> set); | ||
} | ||
|
||
/** | ||
* A node created from the original node, but which has only children from the specified set. | ||
* | ||
* @since 1.1.4 | ||
*/ | ||
private static final class SubNode implements Node { | ||
/** | ||
* Original node. | ||
*/ | ||
private final Node original; | ||
|
||
/** | ||
* Index map containing the indexes of the nodes that should be included | ||
* to the resulting tree. | ||
*/ | ||
private final Map<Node, List<Integer>> indexes; | ||
|
||
/** | ||
* Array of children (also truncated nodes). | ||
*/ | ||
private final SubNode[] children; | ||
|
||
/** | ||
* Constructor. | ||
* @param original Original node | ||
* @param indexes Index map containing the indexes of the nodes that should be included | ||
* to the resulting tree | ||
*/ | ||
private SubNode(final Node original, final Map<Node, List<Integer>> indexes) { | ||
this.original = original; | ||
this.indexes = indexes; | ||
this.children = new SubNode[indexes.get(original).size()]; | ||
} | ||
|
||
@Override | ||
public Fragment getFragment() { | ||
return this.original.getFragment(); | ||
} | ||
|
||
@Override | ||
public Type getType() { | ||
return this.original.getType(); | ||
} | ||
|
||
@Override | ||
public String getData() { | ||
return this.original.getData(); | ||
} | ||
|
||
@Override | ||
public int getChildCount() { | ||
return this.children.length; | ||
} | ||
|
||
@Override | ||
public Node getChild(final int index) { | ||
if (this.children[index] == null) { | ||
this.children[index] = new SubNode( | ||
this.original.getChild(this.indexes.get(this.original).get(index)), | ||
this.indexes | ||
); | ||
} | ||
return this.children[index]; | ||
} | ||
} | ||
} |
59 changes: 59 additions & 0 deletions
59
src/test/java/org/cqfn/astranaut/core/algorithms/NodeSelectorTest.java
This file contains 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,59 @@ | ||
/* | ||
* 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; | ||
|
||
/** | ||
* Test for {@link NodeSelector}. | ||
* | ||
* @since 1.1.4 | ||
*/ | ||
class NodeSelectorTest { | ||
@Test | ||
void test() { | ||
final Node tree = DraftNode.createByDescription("A(B(C(D(E(F)))))"); | ||
final NodeSelector selector = new NodeSelector(tree); | ||
final StringBuilder builder = new StringBuilder(); | ||
final Set<Node> set = selector.select( | ||
(node, parents) -> { | ||
boolean selected = false; | ||
if (node.getTypeName().equals("F")) { | ||
selected = true; | ||
for (final Node parent : parents) { | ||
builder.append(parent.getTypeName()); | ||
} | ||
} | ||
return selected; | ||
} | ||
); | ||
Assertions.assertEquals(1, set.size()); | ||
Assertions.assertEquals("F", set.iterator().next().getTypeName()); | ||
Assertions.assertEquals("EDCBA", builder.toString()); | ||
} | ||
} |
Oops, something went wrong.