spring-hateoas-ext/src/main/java/de/escalon/hypermedia/spring/xhtml/XhtmlWriter.java
private void appendForm(Affordance affordance, ActionDescriptor actionDescriptor) throws IOException {
        String formName = actionDescriptor.getActionName();
        RequestMethod httpMethod = RequestMethod.valueOf(actionDescriptor.getHttpMethod());

        // Link's expand method removes non-required variables from URL
        String actionUrl = affordance.expand()
                .getHref();
        beginForm(OptionalAttributes.attr("action", actionUrl)
                .and("method", getHtmlConformingHttpMethod(httpMethod))
                .and("name", formName));
        write("<h4>");
        write("Form " + formName);
        write("</h4>");

        writeHiddenHttpMethodField(httpMethod);
        // build the form
        if (actionDescriptor.hasRequestBody()) { // parameter bean
            ActionInputParameter requestBody = actionDescriptor.getRequestBody();
            Class<?> parameterType = requestBody.getParameterType();
            recurseBeanProperties(parameterType, actionDescriptor, requestBody, requestBody.getValue(), "");
        } else { // plain parameter list
            Collection<String> requestParams = actionDescriptor.getRequestParamNames();
            for (String requestParamName : requestParams) {
                ActionInputParameter actionInputParameter = actionDescriptor.getActionInputParameter(requestParamName);

                Object[] possibleValues = actionInputParameter.getPossibleValues(actionDescriptor);
                // TODO duplication with appendInputOrSelect
                if (possibleValues.length > 0) {
                    if (actionInputParameter.isArrayOrCollection()) {
                        appendSelectMulti(requestParamName, possibleValues, actionInputParameter);
                    } else {
                        appendSelectOne(requestParamName, possibleValues, actionInputParameter);
                    }
                } else {
                    if (actionInputParameter.isArrayOrCollection()) {
                        // have as many inputs as there are call values, list of 5 nulls gives you five input fields
                        // TODO support for free list input instead, code on demand?
                        Object[] callValues = actionInputParameter.getValues();
                        int items = callValues.length;
                        for (int i = 0; i < items; i++) {
                            Object value;
                            if (i < callValues.length) {
                                value = callValues[i];
                            } else {
                                value = null;
                            }
                            appendInput(requestParamName, actionInputParameter, value, actionInputParameter
									.isReadOnly(requestParamName)); // not readonly
                        }
                    } else {
                        String callValueFormatted = actionInputParameter.getValueFormatted();
                        appendInput(requestParamName, actionInputParameter, callValueFormatted, actionInputParameter
								.isReadOnly(requestParamName)); // not readonly
                    }
                }
            }
        }
        inputButton(Type.SUBMIT, capitalize(httpMethod.name()
                .toLowerCase()));
        endForm();
    }
private void appendForm(Affordance affordance, ActionDescriptor actionDescriptor) throws IOException {
        String formName = actionDescriptor.getActionName();
        RequestMethod httpMethod = RequestMethod.valueOf(actionDescriptor.getHttpMethod());

        // Link's expand method removes non-required variables from URL
        String actionUrl = affordance.expand()
                .getHref();
        beginForm(OptionalAttributes.attr("action", actionUrl)
                .and("method", getHtmlConformingHttpMethod(httpMethod))
                .and("name", formName));
        write("<h4>");
        write("Form " + formName);
        write("</h4>");

        writeHiddenHttpMethodField(httpMethod);
        // build the form
        if (actionDescriptor.hasRequestBody()) { // parameter bean
            ActionInputParameter requestBody = actionDescriptor.getRequestBody();
            Class<?> parameterType = requestBody.getParameterType();
            recurseBeanProperties(parameterType, actionDescriptor, requestBody, requestBody.getValue(), "");
        } else { // plain parameter list
            Collection<String> requestParams = actionDescriptor.getRequestParamNames();
            for (String requestParamName : requestParams) {
                ActionInputParameter actionInputParameter = actionDescriptor.getActionInputParameter(requestParamName);

                Object[] possibleValues = actionInputParameter.getPossibleValues(actionDescriptor);
                // TODO duplication with appendInputOrSelect
                if (possibleValues.length > 0) {
                    if (actionInputParameter.isArrayOrCollection()) {
                        appendSelectMulti(requestParamName, possibleValues, actionInputParameter);
                    } else {
                        appendSelectOne(requestParamName, possibleValues, actionInputParameter);
                    }
                } else {
                    if (actionInputParameter.isArrayOrCollection()) {
                        // have as many inputs as there are call values, list of 5 nulls gives you five input fields
                        // TODO support for free list input instead, code on demand?
                        Object[] callValues = actionInputParameter.getValues();
                        int items = callValues.length;
                        for (int i = 0; i < items; i++) {
                            Object value;
                            if (i < callValues.length) {
                                value = callValues[i];
                            } else {
                                value = null;
                            }
                            appendInput(requestParamName, actionInputParameter, value, actionInputParameter
									.isReadOnly(requestParamName)); // not readonly
                        }
                    } else {
                        String callValueFormatted = actionInputParameter.getValueFormatted();
                        appendInput(requestParamName, actionInputParameter, callValueFormatted, actionInputParameter
								.isReadOnly(requestParamName)); // not readonly
                    }
                }
            }
        }
        inputButton(Type.SUBMIT, capitalize(httpMethod.name()
                .toLowerCase()));
        endForm();
    }
Appends form and squashes non-GET or POST to POST. If required, adds _method field for handling by an
appropriate
filter such as Spring's HiddenHttpMethodFilter.

@param affordance
to make into a form
@param actionDescriptor
describing the form action
@throws IOException
@see

<a href="http://docs.spring.io/spring/docs/3.0 .x/javadoc-api/org/springframework/web/filter/HiddenHttpMethodFilter.html">Spring
MVC HiddenHttpMethodFilter</a>