Skip to content

Commit

Permalink
More tests and fixed JSON deserializer
Browse files Browse the repository at this point in the history
  • Loading branch information
kniazkov committed Aug 30, 2024
1 parent 383a1f3 commit 590abf0
Show file tree
Hide file tree
Showing 7 changed files with 321 additions and 87 deletions.
19 changes: 17 additions & 2 deletions src/main/java/org/cqfn/astranaut/core/base/ActionList.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.cqfn.astranaut.core.algorithms.DiffTreeBuilder;
import org.cqfn.astranaut.core.utils.Promise;

/**
* List of actions to be added to the tree after deserialization to produce a difference tree.
Expand Down Expand Up @@ -64,7 +66,20 @@ public ActionList() {
* @return Checking result
*/
public boolean hasActions() {
return !this.insert.isEmpty() || !this.replace.isEmpty() || !this.delete.isEmpty();
return !this.insert.isEmpty() || !this.replace.isEmpty() || !this.delete.isEmpty();
}

/**
* Adds the node to the list of nodes to be inserted when the parent node is unknown.
* @param node Node to be inserted
* @param after Node after which to insert
* @return A promise to set a parent node to be fulfilled later.
*/
@SuppressWarnings("PMD.UselessQualifiedThis")
public Promise<Node> insertNodeAfter(final Node node, final Node after) {
return new Promise<>(
into -> ActionList.this.insert.add(new Insertion(node, into, after))
);
}

/**
Expand All @@ -74,7 +89,7 @@ public boolean hasActions() {
* @param after Node after which to insert
*/
public void insertNodeAfter(final Node node, final Node into, final Node after) {
this.insert.add(new Insertion(node, into, after));
this.insert.add(new Insertion(node, Objects.requireNonNull(into), after));
}

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

/**
* An object containing a variable whose value is currently unknown but will be known later.
* @param <T> The type of the variable
* @since 2.0.0
*/
public final class Promise<T> {
/**
* Callback that is called when the value of the variable is set.
*/
private final Callback<T> callback;

/**
* Constructor.
* @param callback Callback that is called when the value of the variable is set
*/
public Promise(final Callback<T> callback) {
this.callback = callback;
}

/**
* Sets the new value.
* @param value New value
*/
public void set(final T value) {
this.callback.process(value);
}

/**
* Callback that is called when the value of the variable is set.
* @param <T> The type of the variable
* @since 2.0.0
*/
public interface Callback<T> {
/**
* Method that is called when the value of the variable is set.
* @param value Value of the variable
*/
void process(T value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.cqfn.astranaut.core.base.Insert;
import org.cqfn.astranaut.core.base.Node;
import org.cqfn.astranaut.core.base.Replace;
import org.cqfn.astranaut.core.utils.Promise;

/**
* Node descriptor represented as it is stored in the JSON file.
Expand Down Expand Up @@ -74,101 +75,143 @@ public class NodeDescriptor {
/**
* Converts descriptor into node.
* @param factory The node factory
* @param actions List of actions to be added to the tree after deserialization
* to produce a difference tree
* @param holes A set of nodes that need to be replaced with holes
* @param actions List of actions to be added to the tree after deserialization to produce
* a difference tree
* @param holes Set of nodes that need to be replaced with holes
* @return A node
*/
public Node convert(final Factory factory, final ActionList actions,
final Map<Node, Integer> holes) {
final Node result;
if (this.type.equals(NodeDescriptor.STR_HOLE)) {
result = this.convertHole(factory, actions, holes);
} else {
result = this.convertUsingBuilder(factory, actions, holes);
}
return result;
public Node convert(final Factory factory,
final ActionList actions, final Map<Node, Integer> holes) {
final Converter converter = new Converter(factory, actions, holes);
return converter.convert(this);
}

/**
* Converts descriptor into hole.
* @param factory The node factory
* @param actions List of actions to be added to the tree after deserialization
* to produce a difference tree
* @param holes A set of nodes that need to be replaced with holes
* @return Prototype of a hole, i.e., a node turned into hole
* Converter that converts descriptors into nodes.
* @since 2.0.0
*/
private Node convertHole(final Factory factory, final ActionList actions,
final Map<Node, Integer> holes) {
Node original = DummyNode.INSTANCE;
if (this.prototype != null && this.number != null) {
original = this.prototype.convert(factory, actions, holes);
holes.put(original, this.number);
private static final class Converter {
/**
* The node factory.
*/
private final Factory factory;

/**
* List of actions to be added to the tree after deserialization to produce
* a difference tree.
*/
private final ActionList actions;

/**
* Set of nodes that need to be replaced with holes.
*/
private final Map<Node, Integer> holes;

/**
* Constructor.
* @param factory The node factory
* @param actions List of actions to be added to the tree after deserialization to produce
* a difference tree
* @param holes Set of nodes that need to be replaced with holes
*/
private Converter(final Factory factory,
final ActionList actions, final Map<Node, Integer> holes) {
this.factory = factory;
this.actions = actions;
this.holes = holes;
}
return original;
}

/**
* Converts descriptor into node using {@link Builder} interface.
* @param factory The node factory
* @param actions List of actions to be added to the tree after deserialization
* to produce a difference tree
* @param holes A set of nodes that need to be replaced with holes
* @return A node
*/
private Node convertUsingBuilder(final Factory factory, final ActionList actions,
final Map<Node, Integer> holes) {
Node result = DummyNode.INSTANCE;
final Builder builder = factory.createBuilder(this.type);
if (builder != null) {
if (this.data != null) {
builder.setData(this.data);
/**
* Converts descriptor into node.
* @param descriptor A descriptor that describes a node
* @return A node
*/
public Node convert(final NodeDescriptor descriptor) {
final Node result;
if (descriptor.type.equals(NodeDescriptor.STR_HOLE)) {
result = this.convertHole(descriptor);
} else {
result = this.convertUsingBuilder(descriptor);
}
boolean filled = true;
if (this.children != null) {
filled = builder.setChildrenList(this.convertChildren(factory, actions, holes));
return result;
}

/**
* Converts descriptor into hole.
* @param descriptor A descriptor that describes a node
* @return Prototype of a hole, i.e., a node turned into hole
*/
private Node convertHole(final NodeDescriptor descriptor) {
Node original = DummyNode.INSTANCE;
if (descriptor.prototype != null && descriptor.number != null) {
original = this.convert(descriptor.prototype);
this.holes.put(original, descriptor.number);
}
if (filled && builder.isValid()) {
result = builder.createNode();
return original;
}

/**
* Converts descriptor into node using {@link Builder} interface.
* @param descriptor A descriptor that describes a node
* @return A node
*/
private Node convertUsingBuilder(final NodeDescriptor descriptor) {
Node result = DummyNode.INSTANCE;
final Builder builder = this.factory.createBuilder(descriptor.type);
if (builder != null) {
Promise<Node> parent = null;
if (descriptor.data != null) {
builder.setData(descriptor.data);
}
boolean filled = true;
if (descriptor.children != null) {
final List<Node> list = new ArrayList<>(descriptor.children.size());
parent = this.convertChildren(descriptor, list);
filled = builder.setChildrenList(list);
}
if (filled && builder.isValid()) {
result = builder.createNode();
}
if (parent != null) {
parent.set(result);
}
}
return result;
}
return result;
}

/**
* Converts child descriptors to a list of nodes.
* @param factory The node factory
* @param actions List of actions to be added to the tree after deserialization
* to produce a difference tree
* @param holes A set of nodes that need to be replaced with holes
* @return List of child nodes
*/
private List<Node> convertChildren(final Factory factory, final ActionList actions,
final Map<Node, Integer> holes) {
final List<Node> list = new ArrayList<>(this.children.size());
for (final NodeDescriptor child : this.children) {
final Node converted = child.convert(factory, actions, holes);
if (converted instanceof Insert) {
final Node node = ((Insert) converted).getAfter();
Node after = null;
final int size = list.size();
if (size > 0) {
after = list.get(size - 1);
/**
* Converts child descriptors to a list of nodes.
* @param descriptor A descriptor that describes a node
* @param list Resulting list of converted nodes
* @return List of child nodes
*/
private Promise<Node> convertChildren(final NodeDescriptor descriptor,
final List<Node> list) {
Promise<Node> parent = null;
for (final NodeDescriptor child : descriptor.children) {
final Node converted = this.convert(child);
if (converted instanceof Insert) {
final Node node = ((Insert) converted).getAfter();
Node after = null;
final int size = list.size();
if (size > 0) {
after = list.get(size - 1);
}
parent = this.actions.insertNodeAfter(node, after);
} else if (converted instanceof Replace) {
final Replace action = (Replace) converted;
final Node node = action.getBefore();
list.add(node);
this.actions.replaceNode(node, action.getAfter());
} else if (converted instanceof Delete) {
final Node node = ((Delete) converted).getBefore();
list.add(node);
this.actions.deleteNode(node);
} else {
list.add(converted);
}
actions.insertNodeAfter(node, null, after);
} else if (converted instanceof Replace) {
final Replace action = (Replace) converted;
final Node node = action.getBefore();
list.add(node);
actions.replaceNode(node, action.getAfter());
} else if (converted instanceof Delete) {
final Node node = ((Delete) converted).getBefore();
list.add(node);
actions.deleteNode(node);
} else {
list.add(converted);
}
return parent;
}
return list;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class DiffTreeBuilderTest {
void testTreeWithInsertedNode() {
final Node before = LittleTrees.createStatementListWithTwoChildren();
final Node after = LittleTrees.createStatementListWithThreeChildren(
LittleTrees.createIntegerLiteral(3)
LittleTrees.createIntegerLiteral(2)
);
final DiffTreeBuilder builder = new DiffTreeBuilder(before);
final boolean result = builder.build(after, TopDownMapper.INSTANCE);
Expand Down
Loading

0 comments on commit 590abf0

Please sign in to comment.