/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package javax.faces.component; import static com.sun.faces.util.Util.coalesce; import static javax.faces.component.UIViewRoot.UNIQUE_ID_PREFIX; import static javax.faces.component.visit.VisitResult.COMPLETE; import java.util.Collection; import java.util.Iterator; import javax.faces.application.Application; import javax.faces.component.visit.VisitCallback; import javax.faces.component.visit.VisitContext; import javax.faces.context.FacesContext; import javax.faces.event.PostValidateEvent; import javax.faces.event.PreValidateEvent; /** * <p> * <strong class="changed_modified_2_1">UIForm</strong> is a {@link UIComponent} that represents an * input form to be presented to the user, and whose child components represent (among other things) * the input fields to be included when the form is submitted. * </p> * * <p> * By default, the <code>rendererType</code> property must be set to * "<code>javax.faces.Form</code>". This value can be changed by calling the * <code>setRendererType()</code> method. * </p> */ public class UIForm extends UIComponentBase implements NamingContainer, UniqueIdVendor { // ------------------------------------------------------ Manifest Constants /** * <p> * The standard component type for this component. * </p> */ public static final String COMPONENT_TYPE = "javax.faces.Form"; /** * <p> * The standard component family for this component. * </p> */ public static final String COMPONENT_FAMILY = "javax.faces.Form"; /** * Properties that are tracked by state saving. */ enum PropertyKeys { /** * <p> * The prependId flag. * </p> */ prependId, /** * <p> * Last id vended by * {@link UIForm#createUniqueId(javax.faces.context.FacesContext, String)}. * </p> */ lastId, submitted, } // ------------------------------------------------------------ Constructors /** * <p> * Create a new {@link UIForm} instance with default property values. * </p> */ public UIForm() { super(); setRendererType("javax.faces.Form"); } // -------------------------------------------------------------- Properties @Override public String getFamily() { return COMPONENT_FAMILY; } /** * <p> * <span class="changed_modified_2_1">Returns</span> the current value of the * <code>submitted</code> property. The default value is <code>false</code>. See * {@link #setSubmitted} for details. * </p> * * <p class="changed_modified_2_1"> * This property must be kept as a transient property using the * {@link UIComponent#getTransientStateHelper}. * </p> * * @return <code>true</code> if the form was submitted, <code>false</code> otherwise. */ public boolean isSubmitted() { return (Boolean) getTransientStateHelper().getTransient(PropertyKeys.submitted, false); } /** * <p> * <span class="changed_modified_2_1">If</span> <strong>this</strong> <code>UIForm</code> * instance (as opposed to other forms in the page) is experiencing a submit during this request * processing lifecycle, this method must be called, with <code>true</code> as the argument, * during the {@link UIComponent#decode} for this <code>UIForm</code> instance. If * <strong>this</strong> <code>UIForm</code> instance is <strong>not</strong> experiencing a * submit, this method must be called, with <code>false</code> as the argument, during the * {@link UIComponent#decode} for this <code>UIForm</code> instance. * </p> * * <p> * The value of a <code>UIForm</code>'s submitted property must not be saved as part of its * state. * </p> * * <p class="changed_modified_2_1"> * This property must be kept as a transient property using the * {@link UIComponent#getTransientStateHelper}. * </p> * * @param submitted the new value of the submitted flag. */ public void setSubmitted(boolean submitted) { getTransientStateHelper().putTransient(PropertyKeys.submitted, submitted); } /** * Is the id prepended. * * @return <code>true</code> if it is, <code>false</code> otherwise. */ public boolean isPrependId() { return (Boolean) getStateHelper().eval(PropertyKeys.prependId, true); } public void setPrependId(boolean prependId) { getStateHelper().put(PropertyKeys.prependId, prependId); } // ----------------------------------------------------- UIComponent Methods /** * <p> * Override {@link UIComponent#processDecodes} to ensure that the form is decoded * <strong>before</strong> its children. This is necessary to allow the <code>submitted</code> * property to be correctly set. * </p> * * @throws NullPointerException {@inheritDoc} */ @Override public void processDecodes(FacesContext context) { if (context == null) { throw new NullPointerException(); } // Process this component itself decode(context); // If we're not the submitted form, don't process children. if (!isSubmitted()) { return; } // Process all facets and children of this component Iterator<UIComponent> kids = getFacetsAndChildren(); while (kids.hasNext()) { kids.next().processDecodes(context); } } /** * <p class="changed_modified_2_3"> * Override {@link UIComponent#processValidators} to ensure that the children of this * <code>UIForm</code> instance are only processed if {@link #isSubmitted} returns * <code>true</code>. * </p> * * @throws NullPointerException {@inheritDoc} * @see javax.faces.event.PreValidateEvent * @see javax.faces.event.PostValidateEvent */ @Override public void processValidators(FacesContext context) { if (context == null) { throw new NullPointerException(); } if (!isSubmitted()) { return; } pushComponentToEL(context, this); Application application = context.getApplication(); application.publishEvent(context, PreValidateEvent.class, this); // Process all the facets and children of this component Iterator<UIComponent> kids = getFacetsAndChildren(); while (kids.hasNext()) { kids.next().processValidators(context); } application.publishEvent(context, PostValidateEvent.class, this); popComponentFromEL(context); } /** * <p> * Override {@link UIComponent#processUpdates} to ensure that the children of this * <code>UIForm</code> instance are only processed if {@link #isSubmitted} returns * <code>true</code>. * </p> * * @throws NullPointerException {@inheritDoc} */ @Override public void processUpdates(FacesContext context) { if (context == null) { throw new NullPointerException(); } if (!isSubmitted()) { return; } pushComponentToEL(context, this); try { // Process all facets and children of this component Iterator<UIComponent> kids = getFacetsAndChildren(); while (kids.hasNext()) { kids.next().processUpdates(context); } } finally { popComponentFromEL(context); } } /** * <p class="changed_modified_2_2"> * Generate an identifier for a component. The identifier will be prefixed with * UNIQUE_ID_PREFIX, and will be unique within this component-container. Optionally, a unique * seed value can be supplied by component creators which should be included in the generated * unique id. * </p> * <p class="changed_added_2_2"> * If the <code>prependId</code> property has the value <code>false</code>, this method must * call <code>createUniqueId</code> on the next ancestor <code>UniqueIdVendor</code>. * </p> * * @param context FacesContext * @param seed an optional seed value - e.g. based on the position of the component in the * VDL-template * @return a unique-id in this component-container */ @Override public String createUniqueId(FacesContext context, String seed) { if (isPrependId()) { int lastId = coalesce(getLastId(), 0); setLastId(++lastId); return UNIQUE_ID_PREFIX + coalesce(seed, lastId); } UIComponent ancestorNamingContainer = getParent() == null ? null : getParent().getNamingContainer(); if (ancestorNamingContainer instanceof UniqueIdVendor) { return ((UniqueIdVendor) ancestorNamingContainer).createUniqueId(context, seed); } return context.getViewRoot().createUniqueId(context, seed); } /** * <p> * Override the {@link UIComponent#getContainerClientId} to allow users to disable this form * from prepending its <code>clientId</code> to its descendent's <code>clientIds</code> * depending on the value of this form's {@link #isPrependId} property. * </p> */ @Override public String getContainerClientId(FacesContext context) { if (isPrependId()) { return super.getContainerClientId(context); } UIComponent parent = getParent(); while (parent != null) { if (parent instanceof NamingContainer) { return parent.getContainerClientId(context); } parent = parent.getParent(); } return null; } /** * @see UIComponent#visitTree */ @Override public boolean visitTree(VisitContext context, VisitCallback callback) { // NamingContainers can optimize partial tree visits by taking advantage // of the fact that it is possible to detect whether any ids to visit // exist underneath the NamingContainer. If no such ids exist, there // is no need to visit the subtree under the NamingContainer. // UIForm is a bit different from other NamingContainers. It only acts // as a NamingContainer when prependId is true. Note that if it // weren't for this, we could push this implementation up in to // UIComponent and share it across all NamingContainers. Instead, // we currently duplicate this implementation in UIForm and // UINamingContainer, so that we can check isPrependId() here. if (!isPrependId()) { return super.visitTree(context, callback); } Collection<String> idsToVisit = context.getSubtreeIdsToVisit(this); // If we have ids to visit, let the superclass implementation // handle the visit if (!idsToVisit.isEmpty()) { return super.visitTree(context, callback); } // If we have no child ids to visit, just visit ourselves, if // we are visitable. if (isVisitable(context)) { FacesContext facesContext = context.getFacesContext(); pushComponentToEL(facesContext, null); try { return context.invokeVisitCallback(this, callback) == COMPLETE; } finally { popComponentFromEL(facesContext); } } // Done visiting this subtree. Return false to allow // visit to continue. return false; } // ----------------------------------------------------- Private Methods private Integer getLastId() { return (Integer) getStateHelper().get(PropertyKeys.lastId); } private void setLastId(Integer lastId) { getStateHelper().put(PropertyKeys.lastId, lastId); } }