'Parse a parent element whose children are inputs -- these could be
groups (repeat, conditional) or param elements. Groups will be parsed
recursively.'

def parse_input_elem(self, page_source, enctypes, context=None):

    rval = odict()
    context = ExpressionContext(rval, context)
    for input_source in page_source.parse_input_sources():
        input_type = input_source.parse_input_type()
        if (input_type == 'repeat'):
            group = Repeat()
            group.name = input_source.get('name')
            group.title = input_source.get('title')
            group.help = input_source.get('help', None)
            page_source = input_source.parse_nested_inputs_source()
            group.inputs = self.parse_input_elem(page_source, enctypes, context)
            group.default = int(input_source.get('default', 0))
            group.min = int(input_source.get('min', 0))
            group.max = float(input_source.get('max', 'inf'))
            assert (group.min <= group.max), ValueError('Min    repeat    count    must    be    less-than-or-equal    to    the    max.')
            group.default = min(max(group.default, group.min), group.max)
            rval[group.name] = group
        elif (input_type == 'conditional'):
            group = Conditional()
            group.name = input_source.get('name')
            group.value_ref = input_source.get('value_ref', None)
            group.value_ref_in_group = input_source.get_bool('value_ref_in_group', True)
            value_from = input_source.get('value_from', None)
            if value_from:
                value_from = value_from.split(':')
                group.value_from = locals().get(value_from[0])
                group.test_param = rval[group.value_ref]
                group.test_param.refresh_on_change = True
                for attr in value_from[1].split('.'):
                    group.value_from = getattr(group.value_from, attr)
                for (case_value, case_inputs) in group.value_from(context, group, self).items():
                    case = ConditionalWhen()
                    case.value = case_value
                    if case_inputs:
                        page_source = XmlPageSource(ElementTree.XML(('<when>%s</when>' % case_inputs)))
                        case.inputs = self.parse_input_elem(page_source, enctypes, context)
                    else:
                        case.inputs = odict()
                    group.cases.append(case)
            else:
                test_param_input_source = input_source.parse_test_input_source()
                group.test_param = self.parse_param_elem(test_param_input_source, enctypes, context)
                if group.test_param.optional:
                    log.warning(('Tool    with    id    %s    declares    a    conditional    test    parameter    as    optional,    this    is    invalid    and    will    be    ignored.' % self.id))
                    group.test_param.optional = False
                possible_cases = list(group.test_param.legal_values)
                group.test_param.refresh_on_change = True
                for (value, case_inputs_source) in input_source.parse_when_input_sources():
                    case = ConditionalWhen()
                    case.value = value
                    case.inputs = self.parse_input_elem(case_inputs_source, enctypes, context)
                    group.cases.append(case)
                    try:
                        possible_cases.remove(case.value)
                    except:
                        log.warning(("Tool    %s:    a    when    tag    has    been    defined    for    '%s    (%s)    -->    %s',    but    does    not    appear    to    be    selectable." % (self.id, group.name, group.test_param.name, case.value)))
                for unspecified_case in possible_cases:
                    log.warning(("Tool    %s:    a    when    tag    has    not    been    defined    for    '%s    (%s)    -->    %s',    assuming    empty    inputs." % (self.id, group.name, group.test_param.name, unspecified_case)))
                    case = ConditionalWhen()
                    case.value = unspecified_case
                    case.inputs = odict()
                    group.cases.append(case)
            rval[group.name] = group
        elif (input_type == 'section'):
            group = Section()
            group.name = input_source.get('name')
            group.title = input_source.get('title')
            group.help = input_source.get('help', None)
            group.expanded = input_source.get_bool('expanded', False)
            page_source = input_source.parse_nested_inputs_source()
            group.inputs = self.parse_input_elem(page_source, enctypes, context)
            rval[group.name] = group
        elif (input_type == 'upload_dataset'):
            elem = input_source.elem()
            group = UploadDataset()
            group.name = elem.get('name')
            group.title = elem.get('title')
            group.file_type_name = elem.get('file_type_name', group.file_type_name)
            group.default_file_type = elem.get('default_file_type', group.default_file_type)
            group.metadata_ref = elem.get('metadata_ref', group.metadata_ref)
            rval[group.file_type_name].refresh_on_change = True
            rval[group.file_type_name].refresh_on_change_values = self.app.datatypes_registry.get_composite_extensions()
            group_page_source = XmlPageSource(elem)
            group.inputs = self.parse_input_elem(group_page_source, enctypes, context)
            rval[group.name] = group
        elif (input_type == 'param'):
            param = self.parse_param_elem(input_source, enctypes, context)
            rval[param.name] = param
            if hasattr(param, 'data_ref'):
                param.ref_input = context[param.data_ref]
            self.input_params.append(param)
    return rval
