Created
May 12, 2016 14:59
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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