Returns three sorted dictionaries (:class:`django.utils.datastructures.SortedDict`).
     * The first is a dictionary of form fields based on the
       :class:`~eulxml.xmlmap.XmlObject` class fields and their types.
     * The second is a sorted dictionary of subform classes for any fields of type
       :class:`~eulxml.xmlmap.fields.NodeField` on the model.
     * The third is a sorted dictionary of formsets for any fields of type
       :class:`~eulxml.xmlmap.fields.NodeListField` on the model.

    Default sorting (within each dictionary) is by XmlObject field creation order.

    Used by :class:`XmlObjectFormType` to set up a new :class:`XmlObjectForm`
    class.

    :param fields: optional list of field names; if specified, only the named fields
                will be returned, in the specified order
    :param exclude: optional list of field names that should not be included on
                the form; if a field is listed in both ``fields`` and ``exclude``,
                it will be excluded
    :param widgets: optional dictionary of widget options to be passed to form
                field constructor, keyed on field name
    :param options: optional :class:`~django.forms.models.ModelFormOptions`.
                if specified then fields, exclude, and widgets will default
                to its values.
    :param declared_subforms: optional dictionary of field names and form classes;
                if specified, the specified form class will be used to initialize
                the corresponding subform (for a :class:`~eulxml.xmlmap.fields.NodeField`)
                or a formset (for a :class:`~eulxml.xmlmap.fields.NodeListField`)
    :param max_num: optional value for the maximum number of times a fieldset should repeat.
    :param max_num: optional value for the number of extra forms to provide.

def formfields_for_xmlobject(model, fields=None, exclude=None, widgets=None, options=None,
        declared_subforms=None, max_num=None, extra=None):
    """
    Returns three sorted dictionaries (:class:`django.utils.datastructures.SortedDict`).
     * The first is a dictionary of form fields based on the
       :class:`~eulxml.xmlmap.XmlObject` class fields and their types.
     * The second is a sorted dictionary of subform classes for any fields of type
       :class:`~eulxml.xmlmap.fields.NodeField` on the model.
     * The third is a sorted dictionary of formsets for any fields of type
       :class:`~eulxml.xmlmap.fields.NodeListField` on the model.

    Default sorting (within each dictionary) is by XmlObject field creation order.

    Used by :class:`XmlObjectFormType` to set up a new :class:`XmlObjectForm`
    class.

    :param fields: optional list of field names; if specified, only the named fields
                will be returned, in the specified order
    :param exclude: optional list of field names that should not be included on
                the form; if a field is listed in both ``fields`` and ``exclude``,
                it will be excluded
    :param widgets: optional dictionary of widget options to be passed to form
                field constructor, keyed on field name
    :param options: optional :class:`~django.forms.models.ModelFormOptions`.
                if specified then fields, exclude, and widgets will default
                to its values.
    :param declared_subforms: optional dictionary of field names and form classes;
                if specified, the specified form class will be used to initialize
                the corresponding subform (for a :class:`~eulxml.xmlmap.fields.NodeField`)
                or a formset (for a :class:`~eulxml.xmlmap.fields.NodeListField`)
    :param max_num: optional value for the maximum number of times a fieldset should repeat.
    :param max_num: optional value for the number of extra forms to provide.
    """

    # first collect fields and excludes for the form and all subforms. base
    # these on the specified options object unless overridden in args.
    fieldlist = getattr(options, 'parsed_fields', None)
    if isinstance(fields, ParsedFieldList):
        fieldlist = fields
    elif fields is not None:
        fieldlist = _parse_field_list(fields, include_parents=True)

    excludelist = getattr(options, 'parsed_exclude', None)
    if isinstance(fields, ParsedFieldList):
        fieldlist = fields
    elif exclude is not None:
        excludelist = _parse_field_list(exclude, include_parents=False)

    if widgets is None and options is not None:
        widgets = options.widgets

    if max_num is None and options is not None:
        max_num = options.max_num

    # collect the fields (unordered for now) that we're going to be returning
    formfields = {}
    subforms = {}
    formsets = {}
    field_order = {}
    subform_labels = {}

    for name, field in six.iteritems(model._fields):
        if fieldlist and not name in fieldlist.fields:
            # if specific fields have been requested and this is not one of them, skip it
            continue
        if excludelist and name in excludelist.fields:
            # if exclude has been specified and this field is listed, skip it
            continue
        if widgets and name in widgets:
            # if a widget has been specified for this field, pass as option to form field init
            kwargs = {'widget': widgets[name] }
        else:
            kwargs = {}
        # get apppropriate form widget based on xmlmap field type
        field_type = None

        # if the xmlmap field knows whether or not it is required, use for form
        if field.required is not None:
            kwargs['required'] = field.required
        if field.verbose_name is not None:
            kwargs['label'] = field.verbose_name
        if field.help_text is not None:
            kwargs['help_text'] = field.help_text

        if hasattr(field, 'choices') and field.choices:
            # if a field has choices defined, use a choice field (no matter what base type)
            field_type = ChoiceField
            kwargs['choices'] = [(val, val) for val in field.choices]
            # FIXME: how to properly do non-required choice field?
            # if field is optional, add a blank choice at the beginning of the list
            if field.required == False and '' not in field.choices:
                # TODO: add an empty_label option (like django ModelChoiceField)
                # to xmlobjectform and pass it in to make this easier to customize
                kwargs['choices'].insert(0, ('', ''))
        elif isinstance(field, xmlmap.fields.StringField):
            field_type = CharField
        elif isinstance(field, xmlmap.fields.IntegerField):
            field_type = IntegerField
        elif isinstance(field, xmlmap.fields.DateField):
            field_type = DateField
        elif isinstance(field, xmlmap.fields.SimpleBooleanField):
            # by default, fields are required - for a boolean, required means it must be checked
            # since that seems nonsensical and not useful for a boolean,
            # setting required to False to allow True or False values
            kwargs['required'] = False
            field_type = BooleanField

        # datefield ? - not yet well-supported; leaving out for now
        # ... should probably distinguish between date and datetime field

        elif isinstance(field, xmlmap.fields.NodeField) or \
            isinstance(field, xmlmap.fields.NodeListField):
            form_label = kwargs['label'] if 'label' in kwargs else fieldname_to_label(name)
            # store subform label in case we can't set on subform/formset
            subform_labels[name] = form_label

             # if a subform class was declared, use that class exactly as is
            if name in declared_subforms:
            	subform = declared_subforms[name]

            # otherwise, define a new xmlobject form for the nodefield or
            # nodelistfield class, using any options passed in for fields under this one
            else:
                subform_opts = {
                    'fields': fieldlist.subfields[name] if fieldlist and name in fieldlist.subfields else None,
                    'exclude': excludelist.subfields[name] if excludelist and name in excludelist.subfields else None,
                    'widgets': widgets[name] if widgets and name in widgets else None,
                    'label': form_label,
                }

                # create the subform class
                subform = xmlobjectform_factory(field.node_class, **subform_opts)

            # store subform or generate and store formset, depending on field type
            if isinstance(field, xmlmap.fields.NodeField):
                subforms[name] = subform
            elif isinstance(field, xmlmap.fields.NodeListField):
                # formset_factory is from django core and we link into it here.
                formsets[name] = formset_factory(subform, formset=BaseXmlObjectFormSet,
                    max_num=subform._meta.max_num, can_delete=subform._meta.can_delete,
                    extra=subform._meta.extra, can_order=subform._meta.can_order)

                formsets[name].form_label = form_label

        elif isinstance(field, xmlmap.fields.StringListField) or \
	    isinstance(field, xmlmap.fields.IntegerListField):
            form_label = kwargs['label'] if 'label' in kwargs else fieldname_to_label(name)

            if isinstance(field, xmlmap.fields.IntegerListField):
            	listform = IntegerListFieldForm
            else:
            	listform = ListFieldForm

            # generate a listfield formset
            formsets[name] = formset_factory(listform, formset=BaseXmlObjectListFieldFormSet)
            # don't need can_delete: since each form is a single field, empty implies delete
            # todo: extra, max_num ? widget?
            formsets[name].form_label = form_label

        # TODO: other list variants


        else:
            # raise exception for unsupported fields
            # currently doesn't handle list fields
            raise Exception('Error on field "%s": XmlObjectForm does not yet support auto form field generation for %s.' \
            	% (name, field.__class__))

        if field_type is not None:
            if 'label' not in kwargs:
                kwargs['label'] = fieldname_to_label(name)
            formfields[name] = field_type(**kwargs)

        # create a dictionary indexed by field creation order, for default field ordering
        field_order[field.creation_counter] = name

    # if fields were explicitly specified, return them in that order
    if fieldlist:
        ordered_fields = SortedDict((name, formfields[name])
                                    for name in fieldlist.fields
                                    if name in formfields)
        ordered_subforms = SortedDict((name, subforms[name])
                                      for name in fieldlist.fields
                                      if name in subforms)
        ordered_formsets = SortedDict((name, formsets[name])
                                      for name in fieldlist.fields
                                      if name in formsets)
    else:
        # sort on field creation counter and generate a django sorted dictionary
        ordered_fields = SortedDict(
            [(field_order[key], formfields[field_order[key]]) for key in sorted(field_order.keys())
                                                if field_order[key] in formfields ]
        )
        ordered_subforms = SortedDict(
            [(field_order[key], subforms[field_order[key]]) for key in sorted(field_order.keys())
                                                if field_order[key] in subforms ]
        )
        ordered_formsets = SortedDict(
            [(field_order[key], formsets[field_order[key]]) for key in sorted(field_order.keys())
                                                if field_order[key] in formsets ]
        )
    return ordered_fields, ordered_subforms, ordered_formsets, subform_labels