-
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 #24 from kniazkov/deep_traversal
Deep traversal algorithms
- Loading branch information
Showing
2 changed files
with
201 additions
and
0 deletions.
There are no files selected for viewing
128 changes: 128 additions & 0 deletions
128
src/main/java/org/cqfn/astranaut/core/algorithms/DeepTraversal.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,128 @@ | ||
/* | ||
* 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.List; | ||
import java.util.Optional; | ||
import org.cqfn.astranaut.core.Node; | ||
|
||
/** | ||
* Performs a deep traversal of the syntax tree. | ||
* | ||
* @since 1.1.5 | ||
*/ | ||
public class DeepTraversal { | ||
/** | ||
* The root node of the tree being traversed. | ||
*/ | ||
private final Node root; | ||
|
||
/** | ||
* Constructor. | ||
* @param root The root node of the tree being traversed | ||
*/ | ||
public DeepTraversal(final Node root) { | ||
this.root = root; | ||
} | ||
|
||
/** | ||
* Processes nodes starting from the root. Processes a node first. | ||
* If the stopping criterion is not reached, recursively processes all children of it, | ||
* starting from the first one. Once a node is found that satisfies the criterion, | ||
* stops traversal.<br/> | ||
* And yes, you can use this algorithm not only to find nodes, but also just to traverse | ||
* the tree in the specific order. | ||
* @param visitor Visitor that processes nodes | ||
* @return Found node (optional) | ||
*/ | ||
public Optional<Node> findFirst(final Visitor visitor) { | ||
return Optional.ofNullable(DeepTraversal.findFirst(this.root, visitor)); | ||
} | ||
|
||
/** | ||
* Processes nodes starting from the root. | ||
* If a node matches the criterion, adds it to the set and does not check | ||
* the children of this node, otherwise it does. | ||
* @param visitor Visitor that processes nodes | ||
* @return List of found nodes (can be empty, but not {@code null}) | ||
*/ | ||
public List<Node> findAll(final Visitor visitor) { | ||
final List<Node> list = new ArrayList<>(0); | ||
DeepTraversal.findAll(this.root, visitor, list); | ||
return list; | ||
} | ||
|
||
/** | ||
* Recursive method that implements the "Find first starting from the root" algorithm. | ||
* @param node Current node to be processed | ||
* @param visitor Visitor that processes nodes | ||
* @return Found node or {@code null} if no node is found | ||
*/ | ||
private static Node findFirst(final Node node, final Visitor visitor) { | ||
Node result = null; | ||
final boolean stop = visitor.process(node); | ||
if (stop) { | ||
result = node; | ||
} else { | ||
final int count = node.getChildCount(); | ||
for (int index = 0; index < count && result == null; index = index + 1) { | ||
result = DeepTraversal.findFirst(node.getChild(index), visitor); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* Recursive method that implements the "Find all starting from the root" algorithm. | ||
* @param node Current node to be processed | ||
* @param visitor Visitor that processes nodes | ||
* @param list List of found nodes | ||
*/ | ||
private static void findAll(final Node node, final Visitor visitor, final List<Node> list) { | ||
final boolean found = visitor.process(node); | ||
if (found) { | ||
list.add(node); | ||
} else { | ||
final int count = node.getChildCount(); | ||
for (int index = 0; index < count; index = index + 1) { | ||
DeepTraversal.findAll(node.getChild(index), visitor, list); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Payload interface for the traversal algorithm. | ||
* | ||
* @since 1.1.5 | ||
*/ | ||
public interface Visitor { | ||
/** | ||
* Processes a node. | ||
* @param node Node | ||
* @return Whether to stop traversal after node processing | ||
*/ | ||
boolean process(Node node); | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
src/test/java/org/cqfn/astranaut/core/algorithms/DeepTraversalTest.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,73 @@ | ||
/* | ||
* 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.List; | ||
import java.util.Optional; | ||
import org.cqfn.astranaut.core.DraftNode; | ||
import org.cqfn.astranaut.core.Node; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
|
||
/** | ||
* Testing {@link DeepTraversal} class. | ||
* | ||
* @since 1.1.5 | ||
*/ | ||
class DeepTraversalTest { | ||
@Test | ||
void testFindFirst() { | ||
final Node root = DraftNode.createByDescription("A(B,C,D(E<\"eee\">,F<\"fff\">)))"); | ||
final DeepTraversal traversal = new DeepTraversal(root); | ||
final Optional<Node> node = traversal.findFirst(node1 -> !node1.getData().isEmpty()); | ||
Assertions.assertTrue(node.isPresent()); | ||
Assertions.assertEquals("E", node.get().getTypeName()); | ||
} | ||
|
||
@Test | ||
void testNotFoundFirst() { | ||
final Node root = DraftNode.createByDescription("A(B,C,D)"); | ||
final DeepTraversal traversal = new DeepTraversal(root); | ||
final Optional<Node> node = traversal.findFirst(node1 -> !node1.getData().isEmpty()); | ||
Assertions.assertFalse(node.isPresent()); | ||
} | ||
|
||
@Test | ||
void testFindAll() { | ||
final Node root = DraftNode.createByDescription("A(B,C<\"ccc\">,D(E<\"eee\">)))"); | ||
final DeepTraversal traversal = new DeepTraversal(root); | ||
final List<Node> list = traversal.findAll(node1 -> !node1.getData().isEmpty()); | ||
Assertions.assertNotNull(list); | ||
Assertions.assertEquals(2, list.size()); | ||
} | ||
|
||
@Test | ||
void testFindNothing() { | ||
final Node root = DraftNode.createByDescription("A(X,Y,Z)"); | ||
final DeepTraversal traversal = new DeepTraversal(root); | ||
final List<Node> list = traversal.findAll(node1 -> !node1.getData().isEmpty()); | ||
Assertions.assertNotNull(list); | ||
Assertions.assertTrue(list.isEmpty()); | ||
} | ||
} |