Skip to content

Instantly share code, notes, and snippets.

@ADelRosarioH
Created May 12, 2016 14:59
Show Gist options
  • Save ADelRosarioH/b789bf0800d4d7809c3dfd86e96c0a27 to your computer and use it in GitHub Desktop.
Save ADelRosarioH/b789bf0800d4d7809c3dfd86e96c0a27 to your computer and use it in GitHub Desktop.
Takes a list object and apply the specified filters and returns a result
public class Transverser {
private String rawJson;
private Object parsedJson;
private final String PATH_TOKEN = '/';
private final String FILTER_START_TOKEN = '[';
private final String FILTER_END_TOKEN = ']';
private final String COMBO_START_TOKEN = '(';
private final String COMBO_END_TOKEN = ')';
private static final String AND_TOKEN_COMPARATOR = '!AND';
private static final String OR_TOKEN_COMPARATOR = '!OR';
private List<Object> results;
public Transverser(String toParseJson){
rawJson = toParseJson;
parsedJson = JSON.deserializeUntyped(toParseJson);
results = new List<Object>();
}
public List<Object> get(String pattern){
List<IFilter> filters = new List<IFilter>();
for(String part: pattern.split(PATH_TOKEN)){
IFilter filter = parse(part);
if(filter != null){
filters.add(filter);
}
}
transverse(filters, null);
return results;
}
private IFilter parse(String part){
IFilter result = null;
if(part.contains(FILTER_START_TOKEN) && part.contains(FILTER_END_TOKEN)){ // Is a filter
String root = part.substringBeforeLast(FILTER_START_TOKEN);
part = part.substringBetween(FILTER_START_TOKEN, FILTER_END_TOKEN).trim();
if(isCombo(part)){ // Is a filter combo
result = createFilterCombo(part);
}else{
result = new Filter(part);
}
}
return result;
}
private Boolean isCombo(String part){
Boolean result = (part.contains(COMBO_START_TOKEN) && part.contains(COMBO_END_TOKEN)) ||
(part.contains(AND_TOKEN_COMPARATOR) || part.contains(OR_TOKEN_COMPARATOR));
System.debug('this part: ' + part);
System.debug('is combo: ' + result);
return result;
}
private IFilter createFilterCombo(String part){
IFilter result = null;
String comparator = null;
List<String> parts = null;
System.debug('create filter with part: ' + part);
integer indexOfAnd = part.indexOf(AND_TOKEN_COMPARATOR);
integer indexOfOr = part.indexOf(OR_TOKEN_COMPARATOR);
comparator = (indexOfAnd > -1) && indexOfAnd < indexOfOr ? AND_TOKEN_COMPARATOR : OR_TOKEN_COMPARATOR;
parts = part.split(comparator, 2);
String left = parts[0];
String right = parts[1];
System.debug('parts: ' + parts);
result = generateFilterCombo(left, comparator, right);
return result;
}
private IFilter generateFilterCombo(String left, String comparator, String right){
IFilter result = null;
left = clear(left);
right = clear(right);
IFilter leftFilter = isCombo(left) ? createFilterCombo(left) : new Filter(left);
IFilter rightFilter = isCombo(right) ? createFilterCombo(right) : new Filter(right);
result = new FilterCombo(leftFilter, comparator, rightFilter);
return result;
}
private String clear(String part){
String result = part.substringBetween(COMBO_START_TOKEN, COMBO_END_TOKEN);
if(result == null){
result = part.remove(COMBO_START_TOKEN);
result = part.remove(COMBO_END_TOKEN);
}
return result;
}
private void apply(List<IFilter> filters, Object o){
for(IFilter filter: filters){
Boolean result = filter.evaluate(o);
if(result){
results.add(o);
}
}
}
public void transverse(List<IFilter> filters, Object objectToTransverse){
objectToTransverse = objectToTransverse == null ? parsedJson : objectToTransverse;
if(objectToTransverse != null){
if(objectToTransverse instanceof List<Object>){
transverseList(filters, (List<Object>)objectToTransverse);
}else if(objectToTransverse instanceof Map<String, Object>){
transverseMap(filters, (Map<String, Object>)objectToTransverse);
}
}
}
private void transverseList(List<IFilter> filters, List<Object> listToTransverse){
for(Object o : listToTransverse){
apply(filters, o);
transverse(filters, o);
}
}
private void transverseMap(List<IFilter> filters, Map<String, Object> mapToTransverse){
for(String key : mapToTransverse.keySet()){
Object o = mapToTransverse.get(key);
apply(filters, o);
transverse(filters, o);
}
}
public interface IFilter {
Boolean evaluate(Object o);
}
public class Filter implements IFilter {
private String left;
private Object right;
private String operator;
private Boolean result = false;
private final List<String> operators = new List<String>{ '=', '!=', '<', '>', '<=', '>=' };
public Filter(String part){
System.debug('simple filter with part: ' + part);
identify(part);
}
public Boolean evaluate(Object data){
Boolean result = false;
System.debug('data to process: ' + data);
if(data instanceof Map<String, Object>) {
Map<String, Object> o = (Map<String, Object>)data;
result = process(o.get(left));
}
if(data instanceof List<Object>) {
for(Object o: (List<Object>)data){
if(process(o)){
result = true;
break;
}
}
}
return result;
}
private Boolean process(Object data){
Boolean result = false;
if(operator == '=') result = equals(data);
if(operator == '!=') result = notEquals(data);
if(operator == '<') result = lessThan(data);
if(operator == '>') result = greaterThan(data);
if(operator == '<=') result = lessOrEquals(data);
if(operator == '>=') result = greaterOrEquals(data);
return result;
}
private void identify(String part){
for(String o : operators){
List<String> parts = part.split(o);
if(parts.size() > 0){
left = parts[0].trim();
operator = o;
right = parts[1];
System.debug('Filter identified: '+ left + ' ' + operator + ' ' + right);
break;
}
}
}
private Boolean equals(Object o){
Boolean r = false;
System.debug('left: ' + String.valueOf(o) + ' right: '+ String.valueOf(right));
if(o == null) return r;
if(right instanceof String && String.valueOf(o).trim() == String.valueOf(right).trim())
r = true;
if(right instanceof Integer && Integer.valueOf(o) == Integer.valueOf(right))
r = true;
if(right instanceof Double && Double.valueOf(o) == Double.valueOf(right))
r = true;
if(right instanceof Boolean && Boolean.valueOf(o) == Boolean.valueOf(right))
r = true;
return r;
}
private Boolean lessThan(Object o){
Boolean r = false;
System.debug('left: ' + String.valueOf(o) + ' right: '+ String.valueOf(right));
if(o == null) return r;
if(right instanceof String && String.valueOf(o).trim() < String.valueOf(right).trim())
r = true;
if(right instanceof Integer && Integer.valueOf(o) < Integer.valueOf(right))
r = true;
if(right instanceof Double && Double.valueOf(o) < Double.valueOf(right))
r = true;
return r;
}
private Boolean greaterThan(Object o){
return !lessThan(o);
}
private Boolean lessOrEquals(Object o){
Boolean r = false;
System.debug('left: ' + String.valueOf(o) + ' right: '+ String.valueOf(right));
if(o == null) return r;
if(right instanceof String && String.valueOf(o).trim() <= String.valueOf(right).trim())
r = true;
if(right instanceof Integer && Integer.valueOf(o) <= Integer.valueOf(right))
r = true;
if(right instanceof Double && Double.valueOf(o) <= Double.valueOf(right))
r = true;
return r;
}
private Boolean greaterOrEquals(Object o){
return !lessOrEquals(o);
}
private Boolean notEquals(Object o){
return !equals(o);
}
}
public class FilterCombo implements IFilter {
private IFilter left;
private String comparator;
private IFilter right;
public FilterCombo(IFilter comboLeft, String comboComparator, IFilter comboRight){
left = comboLeft;
comparator = comboComparator;
right = comboRight;
System.debug('combo comparator: '+ comparator);
}
public Boolean evaluate(Object o){
Boolean result = false;
System.debug('evaluate combo on: ' + o);
if(comparator == AND_TOKEN_COMPARATOR){
result = left.evaluate(o) && right.evaluate(o);
}else if (comparator == OR_TOKEN_COMPARATOR){
result = left.evaluate(o) || right.evaluate(o);
}
return result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment