Skip to content

Commit

Permalink
Merge pull request #17 from kniazkov/diff_tree
Browse files Browse the repository at this point in the history
MOS-997: Added a special function that will allow me to quickly generate test trees from a description
  • Loading branch information
kniazkov authored Mar 26, 2024
2 parents c1521ce + 3436503 commit 44b2af0
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 28 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ SOFTWARE.
<dependency>
<groupId>com.kniazkov</groupId>
<artifactId>json</artifactId>
<version>0.3</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>guru.nidi</groupId>
Expand Down
134 changes: 134 additions & 0 deletions src/main/java/org/cqfn/astranaut/core/DraftNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
package org.cqfn.astranaut.core;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
Expand Down Expand Up @@ -87,6 +89,116 @@ public Node getChild(final int index) {
return this.children.get(index);
}

@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append(this.type.getName());
if (!this.data.isEmpty()) {
builder.append("<\"").append(this.data).append("\">");
}
if (!this.children.isEmpty()) {
boolean flag = false;
builder.append('(');
for (final Node child : this.children) {
if (flag) {
builder.append(", ");
}
flag = true;
builder.append(child.toString());
}
builder.append(')');
}
return builder.toString();
}

/**
* Creates a tree from draft nodes based on description.
* Description format: A(B,C(...),...) where 'A' is the type name
* (it consists only of letters) followed by child nodes (in the same format) in parentheses
* separated by commas.
* @param description Description
* @return Root node of the tree created by description
*/
@SuppressWarnings("PMD.ProhibitPublicStaticMethods")
public static Node createByDescription(final String description) {
final CharacterIterator iterator = new StringCharacterIterator(description);
return DraftNode.createByDescription(iterator);
}

/**
* Creates a tree based on its description (recursive method).
* @param iterator Iterator by description characters
* @return Node of the tree with its children, created by description
*/
private static Node createByDescription(final CharacterIterator iterator) {
char symbol = iterator.current();
final Node result;
final StringBuilder name = new StringBuilder();
while (symbol >= 'A' && symbol <= 'Z' || symbol >= 'a' && symbol <= 'z') {
name.append(symbol);
symbol = iterator.next();
}
if (name.length() > 0) {
final DraftNode.Constructor builder = new DraftNode.Constructor();
builder.setName(name.toString());
if (symbol == '<') {
builder.setData(DraftNode.parseData(iterator));
symbol = iterator.current();
}
if (symbol == '(') {
builder.setChildrenList(DraftNode.parseChildrenList(iterator));
}
result = builder.createNode();
} else {
result = null;
}
return result;
}

/**
* Parses data from description.
* @param iterator Iterator by description characters
* @return Node data, extracted from description
*/
private static String parseData(final CharacterIterator iterator) {
assert iterator.current() == '<';
final StringBuilder data = new StringBuilder();
char symbol = iterator.next();
if (symbol == '\"') {
symbol = iterator.next();
while (symbol != '\"') {
data.append(symbol);
symbol = iterator.next();
}
symbol = iterator.next();
}
if (symbol == '>') {
iterator.next();
}
return data.toString();
}

/**
* Parses children list from description.
* @param iterator Iterator by description characters
* @return Children list, created by description
*/
private static List<Node> parseChildrenList(final CharacterIterator iterator) {
assert iterator.current() == '(';
final List<Node> children = new LinkedList<>();
char next;
do {
iterator.next();
final Node child = DraftNode.createByDescription(iterator);
if (child != null) {
children.add(child);
}
next = iterator.current();
assert next == ')' || next == ',' || next == ' ';
} while (next != ')');
return children;
}

/**
* Type implementation for the draft node.
*
Expand Down Expand Up @@ -220,5 +332,27 @@ public DraftNode createNode() {
node.children = new ArrayList<>(this.children);
return node;
}

@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append(this.name);
if (!this.data.isEmpty()) {
builder.append("<\"").append(this.data).append("\">");
}
if (!this.children.isEmpty()) {
boolean flag = false;
builder.append('(');
for (final Node child : this.children) {
if (flag) {
builder.append(", ");
}
flag = true;
builder.append(child.toString());
}
builder.append(')');
}
return builder.toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ private void mapSubtreesWithTheSameHash(final Node left, final Node right) {
* @param right Related node to the left node
*/
private static void mapSubtreesWithDifferentHashes(final Node left, final Node right) {
final Unprocessed unprocessed = new Unprocessed(left, right);
assert unprocessed.hasNodes();
final Unprocessed counter = new Unprocessed(left, right);
assert counter.hasUnprocessedNodes();
}

/**
Expand Down Expand Up @@ -229,23 +229,23 @@ private static class Unprocessed {
* Checks are there still unprocessed nodes.
* @return Checking result ({@code true} if yes)
*/
boolean hasNodes() {
boolean hasUnprocessedNodes() {
return this.left > 0 && this.right > 0;
}

/**
* Analyzes a case where the only actions that are allowed are additions.
* @return Checking result, {@code true} if we can only add nodes
*/
boolean isJustToAdd() {
boolean onlyActionIsToAddNodes() {
return this.left == 0 && this.add == this.right;
}

/**
* Analyzes a case where the only actions that are allowed are deletions.
* @return Checking result, {@code true} if we can only delete nodes
*/
boolean isJustToDelete() {
boolean onlyActionIsToDeleteNodes() {
return this.right == 0 && this.delete == this.left;
}

Expand Down
90 changes: 90 additions & 0 deletions src/test/java/org/cqfn/astranaut/core/DraftNodeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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;

import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
* Tests covering {@link DraftNode} class.
*
* @since 1.1.0
*/
class DraftNodeTest {
/**
* Testing {@link DraftNode#createByDescription(String)} and
* {@link DraftNode#toString()} methods.
*/
@Test
void createAndSerialize() {
final String[] cases = {
"X",
"X(A)",
"X(A, B, C)",
"X(A(B, C))",
"Addition(Expression, Expression)",
"Node<\"data\">",
};
for (final String test : cases) {
Assertions.assertTrue(DraftNodeTest.createAndSerialize(test));
}
}

/**
* Testing {@link DraftNode.Constructor} class.
*/
@Test
void constructorTest() {
final DraftNode.Constructor ctor = new DraftNode.Constructor();
Assertions.assertEquals("", ctor.toString());
final String name = "Name";
ctor.setName(name);
Assertions.assertEquals(name, ctor.toString());
ctor.setData("data");
Assertions.assertEquals("Name<\"data\">", ctor.toString());
final List<Node> children = Arrays.asList(
DraftNode.createByDescription("A"),
DraftNode.createByDescription("B"),
DraftNode.createByDescription("C")
);
ctor.setChildrenList(children);
final String serialized = "Name<\"data\">(A, B, C)";
Assertions.assertEquals(serialized, ctor.toString());
Assertions.assertEquals(serialized, ctor.createNode().toString());
}

/**
* Testing {@link DraftNode#createByDescription(String)} and
* {@link DraftNode#toString()} methods (testing of one case).
* @param description Syntax tree description
* @return Testing result ({@code true} if passed)
*/
private static boolean createAndSerialize(final String description) {
final Node node = DraftNode.createByDescription(description);
final String serialized = node.toString();
return description.equals(serialized);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
*/
package org.cqfn.astranaut.core.algorithms.mapping;

import java.util.Arrays;
import java.util.Collections;
import org.cqfn.astranaut.core.DraftNode;
import org.cqfn.astranaut.core.Node;
import org.junit.jupiter.api.Assertions;
Expand All @@ -38,29 +36,12 @@
class TopDownMapperTest {
@Test
void testIdenticalTrees() {
final Node first = TopDownMapperTest.createTreeAlpha();
final Node second = TopDownMapperTest.createTreeAlpha();
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 Mapping mapping = mapper.map(first, second);
Assertions.assertEquals(mapping.getRight(first), second);
Assertions.assertEquals(mapping.getLeft(second), first);
}

/**
* Creates tree A(B(C, D)).
* @return Root node
*/
private static Node createTreeAlpha() {
final DraftNode.Constructor ccc = new DraftNode.Constructor();
ccc.setName("C");
final DraftNode.Constructor ddd = new DraftNode.Constructor();
ddd.setName("D");
final DraftNode.Constructor bbb = new DraftNode.Constructor();
bbb.setName("B");
bbb.setChildrenList(Arrays.asList(ccc.createNode(), ddd.createNode()));
final DraftNode.Constructor aaa = new DraftNode.Constructor();
aaa.setName("A");
aaa.setChildrenList(Collections.singletonList(bbb.createNode()));
return aaa.createNode();
}
}

0 comments on commit 44b2af0

Please sign in to comment.