Re: Parser for a simple expression
Ivo wrote:
I need a parser for a simple expression
it must support keywords like AND, NOT and OR and "(" and ")"
constructions.
I have been looking into ANTLR and JavaCC but am really curious if
anyone can point me to an easier method.
If strict validation of your expressions is not required (what I think
is your situation), then the above seems not to be very difficult to
implement without any third-party tools. See the code below (not tested
intensively!).
Regardless of what expression parsing/compiling/processing technique
you'll choose, there is also presented the way I think you could use it
in your code (in this scenario your granted Authorities must simply
implement RoleSet interface).
HTH,
piotr
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
public class Roles {
public interface RoleSet {
boolean contains(String role);
}
public interface CompiledExpression {
boolean matches(RoleSet roles);
}
public static CompiledExpression compile(String expression) {
return new Compiler(new Tokenizer(expression)).compile();
}
static class Tokenizer {
StringTokenizer st;
Tokenizer(String expression) {
st = new StringTokenizer(expression, " ()", true);
}
String nextToken() {
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
if (token.length() > 0) {
return token;
}
}
return null;
}
}
static class Compiler {
Tokenizer tokenizer;
public Compiler(Tokenizer tokenizer) {
this.tokenizer = tokenizer;
}
public CompiledExpression compile() {
return compile(compile(null));
}
CompiledExpression compile(final CompiledExpression left) {
final String token = tokenizer.nextToken();
if(token == null) {
return left;
}
abstract class NextExpression implements CompiledExpression {
CompiledExpression right;
NextExpression(CompiledExpression right) {
this.right = right;
}
}
if(token.equalsIgnoreCase("not")) {
return compile(new NextExpression(compile(null)) {
public boolean matches(RoleSet roles) {
return ! right.matches(roles);
}
});
}
if(token.equalsIgnoreCase("or")) {
return new NextExpression(compile(compile(null))) {
public boolean matches(RoleSet roles) {
return left.matches(roles) || right.matches(roles);
}
};
}
if(token.equalsIgnoreCase("and")) {
return compile(new NextExpression(compile(null)) {
public boolean matches(RoleSet roles) {
return left.matches(roles) && right.matches(roles);
}
});
}
if(token.equals("(")) {
return compile(new NextExpression(compile(null)) {
public boolean matches(RoleSet roles) {
return right.matches(roles);
}
});
}
if(token.equals(")")) {
return left;
}
return new CompiledExpression() {
public boolean matches(RoleSet roles) {
return roles.contains(token);
}
};
}
}
public static void main(String[] args) throws Exception {
final String expression = "a or b and not c";
final Roles.CompiledExpression ce = Roles.compile(expression);
System.out.println("expression: " + expression);
new RoleSet() {
Set<String> roles = new HashSet<String>();
public boolean contains(String role) {
boolean enabled = roles.contains(role);
System.out.println(
"role " + role + " " + (enabled ? "set" : "not set"));
return enabled;
}
void check() {
System.out.println(roles + " " +
(ce.matches(this) ? "matches" : "don't match"));
}
{ // test...
check();
roles.add("b");
check();
roles.add("c");
check();
roles.add("a");
check();
}
};
}
}