Suppose i have an xml containing a number of nested logical operators similar to this structure:
<?xml version="1.0" encoding="UTF-8"?>
<Or>
<And>
<Condition1>
<Condition2>
</And>
<Or>
<And>
<Condition3>
<Condition4>
<Or>
<Condition5>
<Condition6>
</Or>
</And>
<Condition7>
</Or>
<Condition8>
</Or>
There are no limitations to the length or depth of the structure.
I want to represent this structure in Java and be able to determine the boolean value of the root node at any given time. The only way I can think of doing this would be some sort of nested List arrangement, which I'm trying to avoid, because i fear this might be very "messy". Is there a more elegant solution to this?
I faced a similar problem in a project of mine. My solution was to create a type Tree that contained inside it a field called value and a list of Tree, called descendants.
When a node was an operator, value would be filled with the operator name (AND, OR, NOT) and the subconditions would be added to the list of descendants. When it was a condition, the value would contain the condition and the list would be empty.
The class Tree also contains a method evaluate(boolean neutralElement) that returns a boolean. When evaluating the node, I would simply evaluate it if it was not an operator. If it was an operator, I would apply the current operator over its descendants (the neutralElement was necessary because due to project conditions, a descendant could be an empty text, which should be evaluated to false if its parent was OR or true, if its parent was an AND).
That is an interesting problem. In my opinion it is very easy to solve by using JAXB. Here is a little prototype:
import java.io.StringReader;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import org.xml.sax.InputSource;
public class Test {
public static void main(String... args) {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<Or>\r\n" +
" <And>\r\n" +
" <Condition value = 'true'/>\r\n" +
" <Condition value = 'true'/>\r\n" +
" </And>\r\n" +
" <And>\r\n" +
" <Condition value = 'false'/>\r\n" +
" <Condition value = 'true'/>\r\n" +
" <Or>\r\n" +
" <Condition value = 'false'/>\r\n" +
" <Condition value = 'true'/>\r\n" +
" </Or>\r\n" +
" </And>\r\n" +
" <Condition value = 'false'/>\r\n" +
" <Condition value = 'false'/>\r\n" +
"</Or>";
try {
Evaluable o = (Evaluable) JAXBContext.newInstance(BooleanOperators.class, Condition.class).createUnmarshaller()
.unmarshal(new InputSource(new StringReader(xml)));
System.out.println(o);
System.out.println(o.evaluate());
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
interface Evaluable {
static final Evaluable TRUE = of(true);
static final Evaluable FALSE = of(false);
boolean evaluate();
static Evaluable of(boolean result) {
return new Evaluable() {
@Override
public boolean evaluate() {
return result;
}
};
}
}
@XmlAccessorType(XmlAccessType.NONE)
@XmlSeeAlso({ And.class, Or.class })
abstract class BooleanOperators implements Evaluable {
@XmlAnyElement(lax = true)
protected List<Evaluable> evaluables;
@Override
public String toString() {
return getClass().getSimpleName() + " {" + evaluables + "}";
}
}
@XmlRootElement(name = "And")
@XmlAccessorType(XmlAccessType.NONE)
class And extends BooleanOperators {
@Override
public boolean evaluate() {
if (evaluables == null || evaluables.isEmpty()) {
return true;
}
return evaluables.stream().reduce(TRUE, (i, j) -> Evaluable.of(i.evaluate() && j.evaluate())).evaluate();
}
}
@XmlRootElement(name = "Or")
@XmlAccessorType(XmlAccessType.NONE)
class Or extends BooleanOperators {
@Override
public boolean evaluate() {
if (evaluables == null || evaluables.isEmpty()) {
return true;
}
return evaluables.stream().reduce(FALSE, (i, j) -> Evaluable.of(i.evaluate() || j.evaluate())).evaluate();
}
}
@XmlRootElement(name = "Condition")
@XmlAccessorType(XmlAccessType.NONE)
class Condition implements Evaluable {
@XmlAttribute(required = true, name = "value")
private boolean result;
@Override
public boolean evaluate() {
return result;
}
@Override
public String toString() {
return "Condition (" + result + ")";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With