/**
  *
  */
 private static function _processChildren(SearchResult $pr_res, $po_nodes, array $pa_vals, array $pa_options = null)
 {
     if (!is_array($pa_options)) {
         $pa_options = [];
     }
     if (!$po_nodes) {
         return '';
     }
     $vs_acc = '';
     $ps_tablename = $pr_res->tableName();
     $o_dm = Datamodel::load();
     $t_instance = $o_dm->getInstanceByTableName($ps_tablename, true);
     $ps_delimiter = caGetOption('delimiter', $pa_options, '; ');
     $pb_is_case = caGetOption('isCase', $pa_options, false, ['castTo' => 'boolean']);
     $pb_quote = caGetOption('quote', $pa_options, false, ['castTo' => 'boolean']);
     $pa_primary_ids = caGetOption('primaryIDs', $pa_options, null);
     $pb_include_blanks = caGetOption('includeBlankValuesInArray', $pa_options, false);
     unset($pa_options['quote']);
     $vn_last_unit_omit_count = null;
     foreach ($po_nodes as $vn_index => $o_node) {
         switch ($vs_tag = strtolower($o_node->tag)) {
             case 'case':
                 if (!$pb_is_case) {
                     $vs_acc .= DisplayTemplateParser::_processChildren($pr_res, $o_node->children, $pa_vals, array_merge($pa_options, ['isCase' => true]));
                 }
                 break;
             case 'if':
                 if (strlen($vs_rule = $o_node->rule) && ExpressionParser::evaluate($vs_rule, $pa_vals)) {
                     $vs_acc .= DisplayTemplateParser::_processChildren($pr_res, $o_node->children, DisplayTemplateParser::_getValues($pr_res, DisplayTemplateParser::_getTags($o_node->children, $pa_options), $pa_options), $pa_options);
                     if ($pb_is_case) {
                         break 2;
                     }
                 }
                 break;
             case 'ifdef':
             case 'ifnotdef':
                 $vb_defined = DisplayTemplateParser::_evaluateCodeAttribute($pr_res, $o_node, ['index' => caGetOption('index', $pa_options, null), 'mode' => $vs_tag == 'ifdef' ? 'present' : 'not_present']);
                 if ($vs_tag == 'ifdef' && $vb_defined || $vs_tag == 'ifnotdef' && $vb_defined) {
                     // Make sure returned values are not empty
                     $vs_acc .= DisplayTemplateParser::_processChildren($pr_res, $o_node->children, DisplayTemplateParser::_getValues($pr_res, DisplayTemplateParser::_getTags($o_node->children, $pa_options), $pa_options), $pa_options);
                     if ($pb_is_case) {
                         break 2;
                     }
                 }
                 break;
             case 'ifcount':
                 $vn_min = (int) $o_node->min;
                 $vn_max = (int) $o_node->max;
                 if (!is_array($va_codes = DisplayTemplateParser::_getCodesFromAttribute($o_node)) || !sizeof($va_codes)) {
                     break;
                 }
                 $pa_check_access = $t_instance->hasField('access') ? caGetOption('checkAccess', $pa_options, null) : null;
                 if (!is_array($pa_check_access) || !sizeof($pa_check_access)) {
                     $pa_check_access = null;
                 }
                 $vb_bool = DisplayTemplateParser::_getCodesBooleanModeAttribute($o_node);
                 $va_restrict_to_types = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'restrictToTypes']);
                 $va_exclude_types = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'excludeTypes']);
                 $va_restrict_to_relationship_types = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'restrictToRelationshipTypes']);
                 $va_exclude_to_relationship_types = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'excludeRelationshipTypes']);
                 $vm_count = $vb_bool == 'AND' ? 0 : [];
                 foreach ($va_codes as $vs_code) {
                     $va_vals = $pr_res->get($vs_code, ['checkAccess' => $pa_check_access, 'returnAsArray' => true, 'restrictToTypes' => $va_restrict_to_types, 'excludeTypes' => $va_exclude_types, 'restrictToRelationshipTypes' => $va_restrict_to_relationship_types, 'excludeRelationshipTypes' => $va_exclude_to_relationship_types]);
                     if (is_array($va_vals)) {
                         if ($vb_bool == 'AND') {
                             $vm_count += sizeof($va_vals);
                         } else {
                             $vm_count[$vs_code] = sizeof($va_vals);
                         }
                     }
                 }
                 if ($vb_bool == 'AND') {
                     if ($vn_min <= $vm_count && ($vn_max >= $vm_count || !$vn_max)) {
                         $vs_acc .= DisplayTemplateParser::_processChildren($pr_res, $o_node->children, DisplayTemplateParser::_getValues($pr_res, DisplayTemplateParser::_getTags($o_node->children, $pa_options), $pa_options), $pa_options);
                         if ($pb_is_case) {
                             break 2;
                         }
                     }
                 } else {
                     $vb_all_have_count = true;
                     foreach ($vm_count as $vs_code => $vn_count) {
                         if (!($vn_min <= $vn_count && ($vn_max >= $vn_count || !$vn_max))) {
                             $vb_all_have_count = false;
                             break 2;
                         }
                     }
                     if ($vb_all_have_count) {
                         $vs_acc .= DisplayTemplateParser::_processChildren($pr_res, $o_node->children, $pa_vals, $pa_options);
                         if ($pb_is_case) {
                             break 2;
                         }
                     }
                 }
                 break;
             case 'more':
                 // Does a placeholder with value follow this tag?
                 // NOTE: 	We don't take into account <ifdef> and friends when finding a set placeholder; it may be set but not visible due to a conditional
                 // 			This case is not covered at the moment on the assumption that if you're using <more> you're not using conditionals. This may or may not be a good assumption.
                 for ($vn_i = $vn_index + 1; $vn_i < sizeof($po_nodes); $vn_i++) {
                     if ($po_nodes[$vn_i] && $po_nodes[$vn_i]->tag == '~text~' && is_array($va_following_tags = caGetTemplateTags($po_nodes[$vn_i]->text))) {
                         foreach ($va_following_tags as $vs_following_tag) {
                             if (isset($pa_vals[$vs_following_tag]) && strlen($pa_vals[$vs_following_tag])) {
                                 $vs_acc .= DisplayTemplateParser::_processChildren($pr_res, $o_node->children, $pa_vals, $pa_options);
                                 if ($pb_is_case) {
                                     break 2;
                                 }
                             }
                         }
                     }
                 }
                 break;
             case 'between':
                 // Does a placeholder with value precede this tag?
                 // NOTE: 	We don't take into account <ifdef> and friends when finding a set placeholder; it may be set but not visible due to a conditional
                 // 			This case is not covered at the moment on the assumption that if you're using <between> you're not using conditionals. This may or may not be a good assumption.
                 $vb_has_preceding_value = false;
                 for ($vn_i = 0; $vn_i < $vn_index; $vn_i++) {
                     if ($po_nodes[$vn_i] && $po_nodes[$vn_i]->tag == '~text~' && is_array($va_preceding_tags = caGetTemplateTags($po_nodes[$vn_i]->text))) {
                         foreach ($va_preceding_tags as $vs_preceding_tag) {
                             if (isset($pa_vals[$vs_preceding_tag]) && strlen($pa_vals[$vs_preceding_tag])) {
                                 $vb_has_preceding_value = true;
                             }
                         }
                     }
                 }
                 if ($vb_has_preceding_value) {
                     // Does it have a value immediately following it?
                     for ($vn_i = $vn_index + 1; $vn_i < sizeof($po_nodes); $vn_i++) {
                         if ($po_nodes[$vn_i] && $po_nodes[$vn_i]->tag == '~text~' && is_array($va_following_tags = caGetTemplateTags($po_nodes[$vn_i]->text))) {
                             foreach ($va_following_tags as $vs_following_tag) {
                                 if (isset($pa_vals[$vs_following_tag]) && strlen($pa_vals[$vs_following_tag])) {
                                     $vs_acc .= DisplayTemplateParser::_processChildren($pr_res, $o_node->children, $pa_vals, $pa_options);
                                     if ($pb_is_case) {
                                         break 2;
                                     }
                                 }
                                 break;
                             }
                         }
                     }
                 }
                 break;
             case 'expression':
                 if ($vs_exp = trim($o_node->getInnerText())) {
                     $vs_acc .= ExpressionParser::evaluate(DisplayTemplateParser::_processChildren($pr_res, $o_node->children, DisplayTemplateParser::_getValues($pr_res, DisplayTemplateParser::_getTags($o_node->children, $pa_options), $pa_options), array_merge($pa_options, ['quote' => true])), $pa_vals);
                     if ($pb_is_case) {
                         break 2;
                     }
                 }
                 break;
             case 'unit':
                 $va_relative_to_tmp = $o_node->relativeTo ? explode(".", $o_node->relativeTo) : [$ps_tablename];
                 if ($va_relative_to_tmp[0] && !($t_rel_instance = $o_dm->getInstanceByTableName($va_relative_to_tmp[0], true))) {
                     continue;
                 }
                 $vn_last_unit_omit_count = 0;
                 // <unit> attributes
                 $vs_unit_delimiter = $o_node->delimiter ? (string) $o_node->delimiter : $ps_delimiter;
                 $vb_unique = $o_node->unique ? (bool) $o_node->unique : false;
                 $vb_aggregate_unique = $o_node->aggregateUnique ? (bool) $o_node->aggregateUnique : false;
                 $vs_unit_skip_if_expression = (string) $o_node->skipIfExpression;
                 $vn_start = (int) $o_node->start;
                 $vn_length = (int) $o_node->length;
                 $pa_check_access = $t_instance->hasField('access') ? caGetOption('checkAccess', $pa_options, null) : null;
                 if (!is_array($pa_check_access) || !sizeof($pa_check_access)) {
                     $pa_check_access = null;
                 }
                 // additional get options for pulling related records
                 $va_get_options = ['returnAsArray' => true, 'checkAccess' => $pa_check_access];
                 $va_get_options['restrictToTypes'] = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'restrictToTypes']);
                 $va_get_options['excludeTypes'] = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'excludeTypes']);
                 $va_get_options['restrictToRelationshipTypes'] = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'restrictToRelationshipTypes']);
                 $va_get_options['excludeRelationshipTypes'] = DisplayTemplateParser::_getCodesFromAttribute($o_node, ['attribute' => 'excludeRelationshipTypes']);
                 if ($o_node->sort) {
                     $va_get_options['sort'] = preg_split('![ ,;]+!', $o_node->sort);
                     $va_get_options['sortDirection'] = $o_node->sortDirection;
                 }
                 if (sizeof($va_relative_to_tmp) == 1 && $va_relative_to_tmp[0] == $ps_tablename || sizeof($va_relative_to_tmp) >= 1 && $va_relative_to_tmp[0] == $ps_tablename && $va_relative_to_tmp[1] != 'related') {
                     $vs_relative_to_container = null;
                     switch (strtolower($va_relative_to_tmp[1])) {
                         case 'hierarchy':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".hierarchy." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         case 'parent':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".parent." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         case 'children':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".children." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         case 'siblings':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".siblings." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         default:
                             // If relativeTo is not set to a valid attribute try to guess from template
                             if ($t_rel_instance->isValidMetadataElement(join(".", array_slice($va_relative_to_tmp, 1, 1)), true)) {
                                 $vs_relative_to_container = join(".", array_slice($va_relative_to_tmp, 0, 2));
                             } else {
                                 $va_tags = caGetTemplateTags($o_node->getInnerText());
                                 foreach ($va_tags as $vs_tag) {
                                     $va_tag = explode('.', $vs_tag);
                                     while (sizeof($va_tag) > 1) {
                                         $vs_end = array_pop($va_tag);
                                         if ($t_rel_instance->isValidMetadataElement($vs_end, true)) {
                                             $va_tag[] = $vs_end;
                                             $vs_relative_to_container = join(".", $va_tag);
                                             break 2;
                                         }
                                     }
                                 }
                             }
                             $va_relative_ids = array($pr_res->getPrimaryKey());
                             break;
                     }
                     // process template for all records selected by unit tag
                     $va_tmpl_val = DisplayTemplateParser::evaluate($o_node->getInnerText(), $ps_tablename, $va_relative_ids, array_merge($pa_options, ['sort' => $va_get_options['sort'], 'sortDirection' => $va_get_options['sortDirection'], 'returnAsArray' => true, 'delimiter' => $vs_unit_delimiter, 'skipIfExpression' => $vs_unit_skip_if_expression, 'placeholderPrefix' => (string) $o_node->relativeTo, 'restrictToTypes' => $va_get_options['restrictToTypes'], 'excludeTypes' => $va_get_options['excludeTypes'], 'isUnit' => true, 'unitStart' => $vn_start, 'unitLength' => $vn_length, 'relativeToContainer' => $vs_relative_to_container, 'includeBlankValuesInTopLevelForPrefetch' => false, 'unique' => $vb_unique, 'aggregateUnique' => $vb_aggregate_unique]));
                     if ($vb_unique) {
                         $va_tmpl_val = array_unique($va_tmpl_val);
                     }
                     if ($vn_start > 0 || !is_null($vn_length)) {
                         $vn_last_unit_omit_count = sizeof($va_tmpl_val) - ($vn_length - $vn_start);
                     }
                     if (caGetOption('returnAsArray', $pa_options, false)) {
                         return $va_tmpl_val;
                     }
                     $vs_acc .= join($vs_unit_delimiter, $va_tmpl_val);
                     if ($pb_is_case) {
                         break 2;
                     }
                 } else {
                     switch (strtolower($va_relative_to_tmp[1])) {
                         case 'hierarchy':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".hierarchy." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         case 'parent':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".parent." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         case 'children':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".children." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         case 'siblings':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".siblings." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         case 'related':
                             $va_relative_ids = $pr_res->get($t_rel_instance->tableName() . ".related." . $t_rel_instance->primaryKey(), $va_get_options);
                             $va_relative_ids = array_values($va_relative_ids);
                             break;
                         default:
                             if (method_exists($t_instance, 'isSelfRelationship') && $t_instance->isSelfRelationship() && is_array($pa_primary_ids) && isset($pa_primary_ids[$t_rel_instance->tableName()])) {
                                 $va_relative_ids = array_values($t_instance->getRelatedIDsForSelfRelationship($pa_primary_ids[$t_rel_instance->tableName()], array($pr_res->getPrimaryKey())));
                             } else {
                                 $va_relative_ids = $pr_res->get($t_rel_instance->primaryKey(true), $va_get_options);
                                 $va_relative_ids = is_array($va_relative_ids) ? array_values($va_relative_ids) : array();
                             }
                             break;
                     }
                     $va_tmpl_val = DisplayTemplateParser::evaluate($o_node->getInnerText(), $va_relative_to_tmp[0], $va_relative_ids, array_merge($pa_options, ['sort' => $va_unit['sort'], 'sortDirection' => $va_unit['sortDirection'], 'delimiter' => $vs_unit_delimiter, 'returnAsArray' => true, 'skipIfExpression' => $vs_unit_skip_if_expression, 'placeholderPrefix' => (string) $o_node->relativeTo, 'restrictToTypes' => $va_get_options['restrictToTypes'], 'excludeTypes' => $va_get_options['excludeTypes'], 'isUnit' => true, 'unitStart' => $vn_start, 'unitLength' => $vn_length, 'includeBlankValuesInTopLevelForPrefetch' => false, 'unique' => $vb_unique, 'aggregateUnique' => $vb_aggregate_unique]));
                     if ($vb_unique) {
                         $va_tmpl_val = array_unique($va_tmpl_val);
                     }
                     if ($vn_start > 0 || !is_null($vn_length)) {
                         $vn_num_vals = sizeof($va_tmpl_val);
                         $va_tmpl_val = array_slice($va_tmpl_val, $vn_start, $vn_length > 0 ? $vn_length : null);
                         $vn_last_unit_omit_count = $vn_num_vals - ($vn_length - $vn_start);
                     }
                     if (caGetOption('returnAsArray', $pa_options, false)) {
                         return $va_tmpl_val;
                     }
                     $vs_acc .= join($vs_unit_delimiter, $va_tmpl_val);
                     if ($pb_is_case) {
                         break 2;
                     }
                 }
                 break;
             case 'whenunitomits':
                 if ($vn_last_unit_omit_count > 0) {
                     $vs_proc_template = caProcessTemplate($o_node->getInnerText(), array_merge($pa_vals, ['omitcount' => (int) $vn_last_unit_omit_count]), ['quote' => $pb_quote]);
                     $vs_acc .= $vs_proc_template;
                 }
                 break;
             default:
                 if ($o_node->children && sizeof($o_node->children) > 0) {
                     $vs_proc_template = DisplayTemplateParser::_processChildren($pr_res, $o_node->children, $pa_vals, $pa_options);
                 } else {
                     $vs_proc_template = caProcessTemplate($o_node->html(), $pa_vals, ['quote' => $pb_quote]);
                 }
                 if ($vs_tag === 'l') {
                     $va_proc_templates = caCreateLinksFromText(["{$vs_proc_template}"], $ps_tablename, [$pr_res->getPrimaryKey()], null, caGetOption('linkTarget', $pa_options, null), array_merge(['addRelParameter' => true, 'requireLinkTags' => false], $pa_options));
                     $vs_proc_template = array_shift($va_proc_templates);
                 } elseif (strlen($vs_tag) && $vs_tag[0] !== '~') {
                     if ($o_node->children && sizeof($o_node->children) > 0) {
                         $vs_attr = '';
                         if ($o_node->attributes) {
                             foreach ($o_node->attributes as $attribute => $value) {
                                 $vs_attr .= " {$attribute}=\"" . htmlspecialchars(caProcessTemplate($value, $pa_vals, ['quote' => $pb_quote])) . "\"";
                             }
                         }
                         $vs_proc_template = "<{$vs_tag}{$vs_attr}>{$vs_proc_template}</{$vs_tag}>";
                     } elseif ($o_node->attributes && sizeof($o_node->attributes) > 0) {
                         $vs_attr = '';
                         foreach ($o_node->attributes as $attribute => $value) {
                             $vs_attr .= " {$attribute}=\"" . htmlspecialchars(caProcessTemplate($value, $pa_vals, ['quote' => $pb_quote])) . "\"";
                         }
                         switch (strtolower($vs_tag)) {
                             case 'br':
                             case 'hr':
                             case 'meta':
                             case 'link':
                             case 'base':
                             case 'img':
                             case 'embed':
                             case 'param':
                             case 'area':
                             case 'col':
                             case 'input':
                                 $vs_proc_template = "<{$vs_tag}{$vs_attr} />";
                                 break;
                             default:
                                 $vs_proc_template = "<{$vs_tag}{$vs_attr}></{$vs_tag}>";
                                 break;
                         }
                     } else {
                         $vs_proc_template = $o_node->html();
                     }
                 }
                 $vs_acc .= $vs_proc_template;
                 break;
         }
     }
     return $vs_acc;
 }