private Filter parseExpression(String filterExpression) {
   // expressions handled for the moment: (informal grammar)
   // EXP := SIMPLE_EXP | AND_EXP | OR_EXP | NOT_EXP
   // AND_EXP := EXP && EXP
   // OR_EXP := EXP || EXP
   // NOT_EXP := ! EXP
   // SIMPLE_EXP := attname = comma, separated, list, of, accepted, values
   // example: organisation = foo && module = bar, baz
   filterExpression = filterExpression.trim();
   int index = filterExpression.indexOf(AND);
   if (index == -1) {
     index = filterExpression.indexOf(OR);
     if (index == -1) {
       if (filterExpression.startsWith(NOT)) {
         return new NotFilter(parseExpression(filterExpression.substring(NOT.length())));
       } else {
         index = filterExpression.indexOf("=");
         if (index == -1) {
           throw new IllegalArgumentException(
               "bad filter expression: " + filterExpression + ": no equal sign found");
         }
         final String attname = filterExpression.substring(0, index).trim();
         String[] values = filterExpression.substring(index + 1).trim().split(",");
         final Matcher[] matchers = new Matcher[values.length];
         for (int i = 0; i < values.length; i++) {
           matchers[i] = matcher.getMatcher(values[i].trim());
         }
         return new Filter() {
           public boolean accept(Object o) {
             IvyEvent e = (IvyEvent) o;
             String val = (String) e.getAttributes().get(attname);
             if (val == null) {
               return false;
             }
             for (int i = 0; i < matchers.length; i++) {
               if (matchers[i].matches(val)) {
                 return true;
               }
             }
             return false;
           }
         };
       }
     } else {
       return new OrFilter(
           parseExpression(filterExpression.substring(0, index)),
           parseExpression(filterExpression.substring(index + OR.length())));
     }
   } else {
     return new AndFilter(
         parseExpression(filterExpression.substring(0, index)),
         parseExpression(filterExpression.substring(index + AND.length())));
   }
 }