class ReceivedMessageConfig implements ConditionConfig {

   @Override
   public Condition generate(Map<String, Object> values) {
      Preconditions.checkState(messageType != null, "must specify a message type");
      
      Predicate<Address> fromPred = this.source == null ?
         Predicates.alwaysTrue() :
         Predicates.equalTo( FunctionFactory.toAddress(this.source.toTemplate(), values) );
      Predicate<String> messageTypePred = Predicates.equalTo( FunctionFactory.toString(this.messageType.toTemplate(), values) );
      Predicate<Map<String,Object>> attributesPred;
      if(this.attributes.isEmpty()) {
         attributesPred = Predicates.alwaysTrue();
      }
      else {
         Map<String, Predicate<? super Object>> matches = new HashMap<>();
         for(Map.Entry<String, TemplatedExpression> attributeAndExpression: this.attributes.entrySet()) {
            String attribute = attributeAndExpression.getKey();
            TemplatedExpression expression = attributeAndExpression.getValue();
            matches.put(
                  attribute,
                  // TODO more options than equalTo?
                  Predicates.equalTo( expression.toTemplate().apply(values) )
            );
         }
         attributesPred = FunctionFactory.containsAll(matches);
      }
      
      ReceivedMessageCondition condition = new ReceivedMessageCondition(messageTypePred,fromPred,attributesPred);
      return condition;
   }

   public  ReceivedMessageConfig();
   public  ReceivedMessageConfig(
         TemplatedExpression sourceExpression,
         TemplatedExpression messageTypeExpression
   );

}