/* * Copyright (c) 2001-2015, Inversoft Inc., All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the License. */ package org.primeframework.mvc.control.form; import java.io.Writer; import java.util.Map; import org.primeframework.mvc.PrimeException; import org.primeframework.mvc.action.ActionInvocation; import org.primeframework.mvc.action.ActionInvocationStore; import org.primeframework.mvc.action.ActionMapper; import org.primeframework.mvc.control.AbstractControl; import org.primeframework.mvc.control.annotation.ControlAttribute; import org.primeframework.mvc.control.annotation.ControlAttributes; import org.primeframework.mvc.servlet.HTTPMethod; import org.primeframework.mvc.servlet.ServletTools; import com.google.inject.Inject; /** * This is the form control that is used for rendering the open and close form tags. * * @author Brian Pontarelli */ @ControlAttributes( required = { @ControlAttribute(name = "action") } ) public class Form extends AbstractControl { private final FormPreparer formPreparer; private final ActionInvocationStore actionInvocationStore; private final ActionMapper actionMapper; private boolean differentURI = false; @Inject public Form(FormPreparer formPreparer, ActionInvocationStore actionInvocationStore, ActionMapper actionMapper) { this.formPreparer = formPreparer; this.actionInvocationStore = actionInvocationStore; this.actionMapper = actionMapper; } /** * Overrides the renderStart in order to change the current ActionInvocation if the action for the form is different * than the current invocation action. * * @param writer The writer to output to. * @param attributes The attributes. * @param dynamicAttributes The dynamic attributes from the tag. */ @Override public void renderStart(Writer writer, Map<String, Object> attributes, Map<String, String> dynamicAttributes) { String action = (String) attributes.get("action"); boolean fullyQualified = action.startsWith("http://") || action.startsWith("https://"); // Handle relative URIs such as 'delete' with a current URI of '/user/' will result in // a new URI of '/user/delete' if (!action.startsWith("/") && !fullyQualified) { String currentURI = currentInvocation().uri(); int index = currentURI.lastIndexOf("/"); if (index >= 0) { action = currentURI.substring(0, index) + "/" + action; } else if (currentURI.equals("")) { action = "/" + action; } } if (!fullyQualified) { String method = (String) attributes.get("method"); HTTPMethod httpMethod = HTTPMethod.GET; if (method != null && !method.toUpperCase().equals("GET") && !method.toUpperCase().equals("POST")) { throw new PrimeException("Invalid method [" + method + "] for form. Only standard GET and POST methods are allowed."); } else if (method != null) { httpMethod = HTTPMethod.valueOf(method.toUpperCase()); } ActionInvocation current = actionInvocationStore.getCurrent(); ActionInvocation actionInvocation = actionMapper.map(httpMethod, action, false); if (actionInvocation == null || actionInvocation.action == null) { throw new PrimeException("The form action [" + action + "] is not a valid URI that maps to an action " + "class by the Prime MVC."); } else if (current == null || current.action == null || current.action.getClass() != actionInvocation.action.getClass()) { actionInvocationStore.setCurrent(actionInvocation); differentURI = true; } } formPreparer.prepare(); // Fix the action URI to include the context path and jsessionid (if one exists) String contextPath = request.getContextPath(); if (contextPath.length() > 0 && !fullyQualified) { action = contextPath + action; } action += ServletTools.getSessionId(request); attributes.put("action", action); // Render super.renderStart(writer, attributes, dynamicAttributes); } /** * Overrides the renderEnd method to pop the action invocation of the form from the stack. * * @param writer The writer to output to. */ @Override public void renderEnd(Writer writer) { if (differentURI) { actionInvocationStore.removeCurrent(); differentURI = false; } super.renderEnd(writer); } /** * @return form-start.ftl */ protected String startTemplateName() { return "form-start.ftl"; } /** * @return form-end.ftl */ protected String endTemplateName() { return "form-end.ftl"; } }