I came to the realisation that it isn't difficult to create my own String Interpolation, like this:
public class XAP_UTIL_MapInterpolation implements XAP_UTIL_InterpolationIntf {
String template;
Map<String, Object> valueByKeyMap;
public XAP_UTIL_MapInterpolation(String phrase, Map<String, Object> valueByKeyMap) {
this.template = phrase;
this.valueByKeyMap = valueByKeyMap;
}
public override String toString() {
String result = this.template;
for (String key : this.valueByKeyMap.keySet()) {
result = result.replace('${' + key + '}', String.valueOf(this.valueByKeyMap.get(key)));
}
return result;
}
}
So I decided to get back to the problem of making a "Where Clause Interface"
But to make this work, I realised we need to move the values out of the Map in the Interpolation object and into the object which does the query, so I came up with this:
public class XAP_DAO_SOQL_Querier {
private static Object object0;
private static Object object1;
private static Object object2;
private static Object object3;
private static Object object4;
private static Object object5;
private static Object object6;
private static Object object7;
private static Object object8;
private static Object object9;
public List<SObject> selectFor(XAP_DAO_SOQL_QueryInterpolation queryInterpolation) {
return Database.query(
this.rebind(queryInterpolation)
);
}
private String rebind(XAP_DAO_SOQL_QueryInterpolation queryInterpolation) {
String localizedTemplate = queryInterpolation.template;
Integer counter = 0;
for (String key : queryInterpolation.objectsByKeyMap.keySet()) {
localizedTemplate = this.rebind(
localizedTemplate,
key,
queryInterpolation.objectsByKeyMap.get(key),
counter++
);
}
return localizedTemplate;
}
private String rebind(String localizedTemplate, String key, Object value, Integer index) {
switch on index {
when 0 { object0 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 1 { object1 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 2 { object2 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 3 { object3 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 4 { object4 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 5 { object5 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 6 { object6 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 7 { object7 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 8 { object8 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
when 9 { object9 = value; return this.rebind(localizedTemplate, key, 'object' + index);}
}
throw new RebindObjectsLimitException(
'XAP_DAO_SOQL_Querier currently only supports ' + LAST_OBJECT_NUMBER + ' bindings.'
+ ' Attempted query requires ' + index + 1 + ' bindings'
);
}
// etc.
}
This works, such that when I anonymously execute:
Id accountId = [
SELECT Id
FROM Account
LIMIT 1
].Id;
Object idSet = new Set<Id>{
accountId
};
XAP_DAO_SOQL_QueryInterpolation qi = new XAP_DAO_SOQL_QueryInterpolation(
'SELECT Name FROM Account WHERE Id IN ${idSet}',
new Map<String, Object>{
'idSet' => idSet
}
);
XAP_DAO_SOQL_Querier querier = new XAP_DAO_SOQL_Querier();
List<Account> accountList = querier.selectFor(qi);
System.debug('##' + accountList[0].Name);
I correctly get back
19:00:09.68 (101658693)|USER_DEBUG|[21]|DEBUG|##Edge Communications
But there are a few problems with this approach:
It isn't scalable (enough object and inner classes need to exist before it can be consumed).
The code is very WET (Write Every Time).
Any ideas?