Skip to content

Commit

Permalink
Merge pull request #19 from GingerYouth/mos-1005
Browse files Browse the repository at this point in the history
First implementation of the subtrees
  • Loading branch information
kniazkov authored Apr 3, 2024
2 parents a29f533 + 7277753 commit b4b0ef8
Show file tree
Hide file tree
Showing 5 changed files with 514 additions and 4 deletions.
130 changes: 130 additions & 0 deletions src/main/java/org/cqfn/astranaut/core/algorithms/NodeSelector.java
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 src/main/java/org/cqfn/astranaut/core/algorithms/Subtree.java
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];
}
}
}
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());
}
}
Loading

0 comments on commit b4b0ef8

Please sign in to comment.