public function doesConditionMatch(HeraldEngine $engine, HeraldRule $rule, HeraldCondition $condition, $field_value)
 {
     $condition_type = $condition->getFieldCondition();
     $condition_value = $condition->getValue();
     switch ($condition_type) {
         case self::CONDITION_CONTAINS:
             // "Contains" can take an array of strings, as in "Any changed
             // filename" for diffs.
             foreach ((array) $field_value as $value) {
                 if (stripos($value, $condition_value) !== false) {
                     return true;
                 }
             }
             return false;
         case self::CONDITION_NOT_CONTAINS:
             return stripos($field_value, $condition_value) === false;
         case self::CONDITION_IS:
             return $field_value == $condition_value;
         case self::CONDITION_IS_NOT:
             return $field_value != $condition_value;
         case self::CONDITION_IS_ME:
             return $field_value == $rule->getAuthorPHID();
         case self::CONDITION_IS_NOT_ME:
             return $field_value != $rule->getAuthorPHID();
         case self::CONDITION_IS_ANY:
             if (!is_array($condition_value)) {
                 throw new HeraldInvalidConditionException(pht('Expected condition value to be an array.'));
             }
             $condition_value = array_fuse($condition_value);
             return isset($condition_value[$field_value]);
         case self::CONDITION_IS_NOT_ANY:
             if (!is_array($condition_value)) {
                 throw new HeraldInvalidConditionException(pht('Expected condition value to be an array.'));
             }
             $condition_value = array_fuse($condition_value);
             return !isset($condition_value[$field_value]);
         case self::CONDITION_INCLUDE_ALL:
             if (!is_array($field_value)) {
                 throw new HeraldInvalidConditionException(pht('Object produced non-array value!'));
             }
             if (!is_array($condition_value)) {
                 throw new HeraldInvalidConditionException(pht('Expected condition value to be an array.'));
             }
             $have = array_select_keys(array_fuse($field_value), $condition_value);
             return count($have) == count($condition_value);
         case self::CONDITION_INCLUDE_ANY:
             return (bool) array_select_keys(array_fuse($field_value), $condition_value);
         case self::CONDITION_INCLUDE_NONE:
             return !array_select_keys(array_fuse($field_value), $condition_value);
         case self::CONDITION_EXISTS:
         case self::CONDITION_IS_TRUE:
             return (bool) $field_value;
         case self::CONDITION_NOT_EXISTS:
         case self::CONDITION_IS_FALSE:
             return !$field_value;
         case self::CONDITION_UNCONDITIONALLY:
             return (bool) $field_value;
         case self::CONDITION_NEVER:
             return false;
         case self::CONDITION_REGEXP:
             foreach ((array) $field_value as $value) {
                 // We add the 'S' flag because we use the regexp multiple times.
                 // It shouldn't cause any troubles if the flag is already there
                 // - /.*/S is evaluated same as /.*/SS.
                 $result = @preg_match($condition_value . 'S', $value);
                 if ($result === false) {
                     throw new HeraldInvalidConditionException(pht('Regular expression is not valid!'));
                 }
                 if ($result) {
                     return true;
                 }
             }
             return false;
         case self::CONDITION_REGEXP_PAIR:
             // Match a JSON-encoded pair of regular expressions against a
             // dictionary. The first regexp must match the dictionary key, and the
             // second regexp must match the dictionary value. If any key/value pair
             // in the dictionary matches both regexps, the condition is satisfied.
             $regexp_pair = null;
             try {
                 $regexp_pair = phutil_json_decode($condition_value);
             } catch (PhutilJSONParserException $ex) {
                 throw new HeraldInvalidConditionException(pht('Regular expression pair is not valid JSON!'));
             }
             if (count($regexp_pair) != 2) {
                 throw new HeraldInvalidConditionException(pht('Regular expression pair is not a pair!'));
             }
             $key_regexp = array_shift($regexp_pair);
             $value_regexp = array_shift($regexp_pair);
             foreach ((array) $field_value as $key => $value) {
                 $key_matches = @preg_match($key_regexp, $key);
                 if ($key_matches === false) {
                     throw new HeraldInvalidConditionException(pht('First regular expression is invalid!'));
                 }
                 if ($key_matches) {
                     $value_matches = @preg_match($value_regexp, $value);
                     if ($value_matches === false) {
                         throw new HeraldInvalidConditionException(pht('Second regular expression is invalid!'));
                     }
                     if ($value_matches) {
                         return true;
                     }
                 }
             }
             return false;
         case self::CONDITION_RULE:
         case self::CONDITION_NOT_RULE:
             $rule = $engine->getRule($condition_value);
             if (!$rule) {
                 throw new HeraldInvalidConditionException(pht('Condition references a rule which does not exist!'));
             }
             $is_not = $condition_type == self::CONDITION_NOT_RULE;
             $result = $engine->doesRuleMatch($rule, $this);
             if ($is_not) {
                 $result = !$result;
             }
             return $result;
         case self::CONDITION_HAS_BIT:
             return ($condition_value & $field_value) === (int) $condition_value;
         case self::CONDITION_NOT_BIT:
             return ($condition_value & $field_value) !== (int) $condition_value;
         default:
             throw new HeraldInvalidConditionException(pht("Unknown condition '%s'.", $condition_type));
     }
 }