Reputation: 509
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?
Upvotes: 3
Views: 211
Reputation: 11740
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 + ")";
}
}
Upvotes: 1
Reputation: 2041
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).
Upvotes: 1