package aQute.bnd.osgi.resource; import java.util.ArrayList; import java.util.List; import org.osgi.framework.VersionRange; public class FilterBuilder { public enum Operator { EQ("="), APPROX("~="), GE(">="), LE("<="); String name; Operator(String name) { this.name = name; } } static class Sub { Sub previous; String op; List<Object> members = new ArrayList<>(); public Sub(String op, Sub current) { this.op = op; this.previous = current; } public String toString() { if (members.isEmpty()) return ""; if (!op.equals("!") && members.size() == 1) return members.get(0).toString(); StringBuilder sb = new StringBuilder(); sb.append("(").append(op); for (Object top : members) { sb.append(top); } sb.append(")"); return sb.toString(); } } Sub current = new Sub("&", null); public FilterBuilder or() { current = new Sub("|", current); return this; } public FilterBuilder and() { current = new Sub("&", current); return this; } public FilterBuilder not() { current = new Sub("!", current); return this; } public FilterBuilder end() { current.previous.members.add(current); current = current.previous; return this; } public FilterBuilder eq(String key, Object value) { simple(key, Operator.EQ, value); return this; } public FilterBuilder neq(String key, Object value) { not(); simple(key, Operator.EQ, value); end(); return this; } public FilterBuilder gt(String key, Object value) { not(); simple(key, Operator.LE, value); end(); return this; } public FilterBuilder lt(String key, Object value) { not(); simple(key, Operator.GE, value); end(); return this; } public FilterBuilder ge(String key, Object value) { simple(key, Operator.GE, value); return this; } public FilterBuilder le(String key, Object value) { simple(key, Operator.LE, value); return this; } public FilterBuilder isSet(String key) { simple(key, Operator.EQ, "*"); return this; } public FilterBuilder approximate(String key, Object value) { simple(key, Operator.APPROX, value); return this; } public FilterBuilder simple(String key, Operator op, Object value) { current.members.add("(" + key + op.name + escape(value) + ")"); return this; } public FilterBuilder literal(String string) { current.members.add(string); return this; } /** * If value must contain one of the characters reverse solidus ('\' \u005C), * asterisk ('*' \u002A), paren- theses open ('(' \u0028) or parentheses * close (')' \u0029), then these characters should be preceded with the * reverse solidus ('\' \u005C) character. Spaces are significant in value. * Space characters are defined by Character.isWhiteSpace(). * * @param value * @return */ static String escape(Object value) { String s = value.toString(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); switch (c) { case '(' : case '\\' : case ')' : sb.append("\\"); // FALL BACK default : sb.append(c); break; } } return sb.toString(); } public String toString() { return current.toString(); } public FilterBuilder isPresent(String key) { return simple(key, Operator.EQ, "*"); } public FilterBuilder in(String key, VersionRange range) { and(); if (range.getLeftType() == '[') ge(key, range.getLeft()); else gt(key, range.getLeft()); if (range.getRightType() == ']') le(key, range.getRight()); else lt(key, range.getRight()); end(); return this; } public FilterBuilder in(String key, aQute.bnd.version.VersionRange range) { and(); if (range.includeLow()) ge(key, range.getLow()); else gt(key, range.getLow()); if (range.includeHigh()) le(key, range.getHigh()); else lt(key, range.getHigh()); end(); return this; } public void endAnd() { if ( !current.op.equals("&")) throw new IllegalStateException("Expected an & but had " + current.op); end(); } public void endOr() { if (!current.op.equals("|")) throw new IllegalStateException("Expected an | but had " + current.op); end(); } }