/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2016 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.view; import static javax.faces.application.ResourceVisitOption.TOP_LEVEL_VIEWS_ONLY; import java.beans.BeanInfo; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.stream.Stream; import javax.faces.application.Resource; import javax.faces.application.ViewVisitOption; import javax.faces.component.UIComponent; import javax.faces.component.UIViewRoot; import javax.faces.context.FacesContext; /** * <p class="changed_added_2_0"><span class="changed_modified_2_0_rev_a * changed_modified_2_1 changed_modified_2_2">The</span> contract that a view declaration * language must implement to interact with the JSF runtime. An * implementation of this class must be thread-safe.</p> * * <div class="changed_added_2_0"> * * <p>Instances of this class are application scoped and must be * obtained from the {@link ViewDeclarationLanguageFactory}.</p> * </div> * * @since 2.0 * */ public abstract class ViewDeclarationLanguage { /** * <p class="changed_added_2_0">Identifier for the JSP view declaration * language.</p> * * @since 2.1 */ public final static String JSP_VIEW_DECLARATION_LANGUAGE_ID = "java.faces.JSP"; /** * <p class="changed_added_2_0">Identifier for the Facelets view * declaration language.</p> * * @since 2.1 */ public final static String FACELETS_VIEW_DECLARATION_LANGUAGE_ID = "java.faces.Facelets"; /** * <p class="changed_added_2_0">Restore a <code>UIViewRoot</code> * from a previously created view. See section JSF.7.7.2 for the * specification of the default implementation.</p> * * @param context the <code>FacesContext</code> for this request. * @param viewId the identifier for a previously rendered view. * * @throws NullPointerException if any of the arguments are * <code>null</code> * * @return the restored view */ public abstract UIViewRoot restoreView(FacesContext context, String viewId); /** * <p class="changed_added_2_0">Return a reference to the view * metadata for the view represented by the argument * <code>viewId</code>, or <code>null</code> if the metadata cannot * be found. See section JSF.7.7.2 for the specification of the * default implementation. Facelets for JSF 2 implementation must * return non-<code>null</code>. JSP implementations must return * <code>null</code>.</p> * * @param context The <code>FacesContext</code> for this request. * @param viewId the view id from whith to extract the metadata * @since 2.0 * * @throws NullPointerException if any of the arguments are * <code>null</code>. * * @throws javax.faces.FacesException if there is an error in * obtaining the metadata * * @return the view metadata */ public abstract ViewMetadata getViewMetadata(FacesContext context, String viewId); /** * <p class="changed_added_2_0"><span class="changed_modified_2_2">Create</span> * a <code>UIViewRoot</code> from the VDL contained in the artifact referenced by the argument * <code>viewId</code>. <span class="changed_modified_2_2">See section JSF.7.7.2 for the specification of * the default implementation.</span></p> * * @param context the <code>FacesContext</code> for this request. * @param viewId the identifier of an artifact that contains the VDL * syntax that describes this view. * * @throws NullPointerException if any of the arguments are * <code>null</code> * * @since 2.0 * * @return the newly created view root */ public abstract UIViewRoot createView(FacesContext context, String viewId); /** * <p class="changed_added_2_0"><span * class="changed_modified_1">Take</span> any actions specific to * this VDL implementation to cause the argument * <code>UIViewRoot</code> which must have been created via a call * to {@link #createView}, to be populated with children.</p> * <div class="changed_added_2_0"> * <p>The Facelets implementation must insure that markup comprising * the view must be executed, with the {@link * javax.faces.component.UIComponent} instances in the view being * encountered in the same depth-first order as in other lifecycle * methods defined on <code>UIComponent</code>, and added to the * view (but not rendered) during the traversal. The runtime must * guarantee that the view must be fully populated before any of the * following happen.</p> * <ul> * * <li><p>The {@link javax.faces.event.PhaseListener#afterPhase} * method of any <code>PhaseListener</code>s attached to the * application is called</p></li> * <li><p>The {@link javax.faces.component.UIViewRoot} phase * listener installed via {@link * javax.faces.component.UIViewRoot#setAfterPhaseListener} or {@link * javax.faces.component.UIViewRoot#addPhaseListener} are called.</p></li> * * </ul> * <p class="changed_modified_2_1">If the <code>root</code> is * already populated with children, the view must still be re-built, * but care must be taken to ensure that the existing components are * correctly paired up with their VDL counterparts in the VDL page. * Also, any system events that would normally be generated during * the adding or removing of components from the view must be * temporarily disabled during the creation of the view and then * re-enabled when the view has been built.</p> * </div> * * @param context the <code>FacesContext</code> for this request * @param root the <code>UIViewRoot</code> to populate with children * using techniques specific to this VDL implementation. * * @throws IOException if view cannot be built for any reason */ public abstract void buildView(FacesContext context, UIViewRoot root) throws IOException; /** * <p class="changed_added_2_0">Render a view rooted at * argument<code>view</code>. See section JSF.7.7.2 for the * specification of the default implementation.</p> * * @param context the <code>FacesContext</code> for this request. * @param view the <code>UIViewRoot</code> from an early call to * {@link #createView} or {@link #restoreView}. * * @throws NullPointerException if any of the arguments are * <code>null</code> * * @throws IOException if the view cannot be rendered for any reason */ public abstract void renderView(FacesContext context, UIViewRoot view) throws IOException; /** * <p class="changed_added_2_0">Return a reference to the component * metadata for the composite component represented by the argument * <code>componentResource</code>, or <code>null</code> if the * metadata cannot be found. See section JSF.7.7.2 for the * specification of the default implementation. JSP implementations * must throw <code>UnsupportedOperationException</code>.</p> * * @param context The <code>FacesContext</code> for this request. * @param componentResource The <code>Resource</code> that represents the component. * @since 2.0 * * @throws NullPointerException if any of the arguments are * <code>null</code>. * * @throws javax.faces.FacesException if there is an error in * obtaining the metadata * * @throws UnsupportedOperationException if this is a JSP VDL * implementation. * * @return the component metadata */ public abstract BeanInfo getComponentMetadata(FacesContext context, Resource componentResource); /** * <p class="changed_added_2_0">Take implementation specific action * to discover a <code>Resource</code> given the argument * <code>componentResource</code>. See section JSF.7.7.2 for the * specification of the default implementation. JSP implementations * must throw <code>UnsupportedOperationException</code>.</p> * * @param context The <code>FacesContext</code> for this request. * @param componentResource The <code>Resource</code> that represents the component. * @since 2.0 * * @throws NullPointerException if any of the arguments are * <code>null</code>. * * @throws javax.faces.FacesException if there is an error in * obtaining the script component resource * @throws UnsupportedOperationException if this is a JSP VDL * implementation. * * @return the {@link Resource} corresponding to the argument {@code * componentResource} */ public abstract Resource getScriptComponentResource(FacesContext context, Resource componentResource); /** * <p class="changed_added_2_2">Create a component given a * {@link ViewDeclarationLanguage} specific * tag library URI and tag name. The runtime must support this method operating * for the Facelets VDL. * Other kinds of {@code ViewDeclarationLanguage} may be supported but are not * required to be supported. For backward compatibility * with decorated {@code ViewDeclrationLanguage} implementations that do * not override this method, a default implementation is provided that returns * {@code null}. However, any implementation that is compliant with the * version of the specification in which this method was introduced must * implement this method. * </p> * * @param context the {@link FacesContext} for this request * @param taglibURI the fully qualified tag library URI that contains the component * @param tagName the name of the tag within that library that exposes the component * @param attributes any name=value pairs that would otherwise have been * given on the markup that would cause the creation of this component or * {@code null} if no attributes need be given. * * @throws NullPointerException if {@code context}, {@code taglibURI}, or * {@code tagName} are {@code null} * * @since 2.2 * * @return the newly created component */ public UIComponent createComponent(FacesContext context, String taglibURI, String tagName, Map<String, Object> attributes) { return null; } /** * <p class="changed_added_2_0"><span * class="changed_modified_2_0_rev_a">Assuming</span> the component * metadata for argument <code>topLevelComponent</code> has been * made available by an earlier call to {@link * ViewDeclarationLanguage#getComponentMetadata}, leverage the * component metadata for the purpose of re-targeting attached * objects from the top level composite component to the individual * {@link AttachedObjectTarget} instances inside the composite * component. This method must be called by the {@link * ViewDeclarationLanguage} implementation when creating the * <code>UIComponent</code> tree when a composite component usage is * encountered.</p> * * <div class="changed_added_2_0"> * * <p>An algorithm semantically equivalent to the following must be * implemented.</p> * *<ul> * *<li><p>Obtain the metadata for the composite component. *Currently this entails getting the value of the {@link *UIComponent#BEANINFO_KEY} component attribute, which will be *an instance of <code>BeanInfo</code>. If the metadata cannot *be found, log an error message and return.</p></li> * *<li><p>Get the <code>BeanDescriptor</code> from the *<code>BeanInfo</code>.</p></li> * *<li><p>Get the value of the {@link *AttachedObjectTarget#ATTACHED_OBJECT_TARGETS_KEY} from the *<code>BeanDescriptor</code>'s <code>getValue()</code> method. *This will be a <code>List<{@link *AttachedObjectTarget}></code>. Let this be *<em>targetList</em>.</p></li> * *<li><p>For each <em>curHandler</em> entry in the argument *<code>handlers</code></p> * *<ul> * *<li><p>Let <em>forAttributeValue</em> be the return from *{@link AttachedObjectHandler#getFor}. </p></li> * *<li><p>For each <em>curTarget</em> entry in *<em>targetList</em>, the first of the following items that *causes a match will take this action:</p> * *<p style="margin-left: 3em;">For each <code>UIComponent</code> in the *list returned from <em>curTarget.getTargets()</em>, call *<em>curHandler.<a *href="AttachedObjectHandler.html#applyAttachedObject">applyAttachedObject()</a></em>, *passing the <code>FacesContext</code> and the *<code>UIComponent</code>.</p> * *<p>and cause this inner loop to terminate.</p> * *<ul> * *<li><p>If <em>curHandler</em> is an instance of {@link *ActionSource2AttachedObjectHandler} and <em>curTarget</em> is an *instance of {@link ActionSource2AttachedObjectTarget}, and *<em>curTarget.getName()</em> is equal to <em>curTargetName</em>, *consider it a match.</p></li> * *<li><p>If <em>curHandler</em> is an instance of {@link *EditableValueHolderAttachedObjectHandler} and <em>curTarget</em> *is an instance of {@link EditableValueHolderAttachedObjectTarget}, *<span class="changed_modified_2_0_rev_a">and *<em>curTarget.getName()</em> is equal to <em>curTargetName</em>, *consider it a match.</span></p></li> * *<li class="changed_modified_2_0_rev_a"><p>If <em>curHandler</em> *is an instance of {@link ValueHolderAttachedObjectHandler} and *<em>curTarget</em> is an instance of {@link *ValueHolderAttachedObjectTarget}, and <em>curTarget.getName()</em> *is equal to <em>curTargetName</em>, consider it a match.</p></li> * *<li><p>If <em>curHandler</em> is an instance of {@link *BehaviorHolderAttachedObjectHandler} and <em>curTarget</em> is an *instance of {@link BehaviorHolderAttachedObjectTarget}, and either *of the following conditions are true,</p> * * <ul> * * <li><em>curHandler.getEventName()</em> is not <code>null</code> * and is equal to <em>curTargetName</em>.</li> * * <li><em>curHandler.getEventName()</em> is <code>null</code> and * <em>curTarget.isDefaultEvent()</em> is <code>true</code>.</li> * * </ul> * *<p>consider it a match.</p></li> * *</ul> *</li> *</ul> *</li> *</ul> * * <p class="changed_modified_2_0_rev_a">The implementation must * support retargeting attached objects from the top level compsite * component to targets that are composite and non-composite * components.</p> * * <p>An implementation is provided that will throw * <code>UnsupportedOperationException</code>. A Faces implementation * compliant with version 2.0 and beyond of the specification must * override this method.</p> * * </div> * * @param context the FacesContext for this request. * * @param topLevelComponent The UIComponent in the view to which the * attached objects must be attached. This UIComponent must have * its component metadata already associated and available from via * the JavaBeans API. * * @param handlers the tag handlers for the attached objects * * @throws NullPointerException if any of the arguments are * <code>null</code>. * * @since 2.0 */ public void retargetAttachedObjects(FacesContext context, UIComponent topLevelComponent, List<AttachedObjectHandler> handlers) { // no-op } /** * <p class="changed_added_2_0">Assuming the component metadata for * argument <code>topLevelComponent</code> has been made available * by an earlier call to {@link * ViewDeclarationLanguage#getComponentMetadata}, leverage the * component metadata for the purpose of re-targeting any method * expressions from the top level component to the appropriate inner * component. For each attribute that is a * <code>MethodExpression</code> (as indicated by the presence of a * "<code>method-signature</code>" attribute and the absence of a * "<code>type</code>" attribute), the following action must be * taken:</p> * * <div class="changed_added_2_0"> * * <ul> * * <li><p>Get the value of the <em>targets</em> attribute. If the * value is a <code>ValueExpression</code> evaluate it. If there is * no <em>targets</em> attribute, let the name of the metadata * element be the evaluated value of the <em>targets * attribute.</em></p></li> * * <li><p>Interpret <em>targets</em> as a space (not tab) separated * list of ids. For each entry in the list:</p> * * <ul> * * <li><p>Find the inner component of the * <em>topLevelComponent</em> with the id equal to * the current list entry. For discussion, this component is called * <em>target</em>. If not found, log and error and continue to * the next attribute.</p></li> * * <li><p>For discussion the declared name of the attribute is * called <em>name</em>.</p></li> * * <li><p>In the attributes map of the * <em>topLevelComponent</em>, look up the entry under the key * <em>name</em>. Assume the result is a * <code>ValueExpression</code>. For discussion, this is * <em>attributeValueExpression</em>. If not found, log an error * and continue to the next attribute.</p></li> * * <li><p>If <em>name</em> is equal to the string "action", or * "actionListener" without the quotes, assume <em>target</em> is * an {@link javax.faces.component.ActionSource2}.</p></li> * * <li><p>If <em>name</em> is equal to the string "validator", or * "valueChangeListener" without the quotes, assume * <em>target</em> is an {@link * javax.faces.component.EditableValueHolder}.</p></li> * * <li><p>Call <code>getExpressionString()</code> on the * <em>attributeValueExpression</em> and use that string to * create a <code>MethodExpression</code> of the appropriate * signature for <em>name</em>.</p></li> * * <li><p>If <em>name</em> is not equal to any of the previously * listed strings, call <code>getExpressionString()</code> on the * <em>attributeValueExpression</em> and use that string to * create a <code>MethodExpression</code> where the signature is * created based on the value of the * "<code>method-signature</code>" attribute of the * <code><composite:attribute /></code> tag.</p></li> * * <li><p>Let the resultant <code>MethodExpression</code> be * called <em>attributeMethodExpression</em> for discussion. * </p></li> * * <li><p>If <em>name</em> is equal to the string "action" * without the quotes, call {@link * javax.faces.component.ActionSource2#setActionExpression} on * <em>target</em>, passing <em>attributeMethodExpression</em>.</p></li> * * <li><p>If <em>name</em> is equal to the string * "actionListener" without the quotes, call {@link * javax.faces.component.ActionSource#addActionListener} on * <em>target</em>, passing <em>attributeMethodExpression</em> * wrapped in a {@link javax.faces.event.MethodExpressionActionListener}.</p></li> * * <li><p>If <em>name</em> is equal to the string * "validator" without the quotes, call {@link * javax.faces.component.EditableValueHolder#addValidator} on <em>target</em>, * passing <em>attributeMethodExpression</em> wrapped in a {@link * javax.faces.validator.MethodExpressionValidator}.</p></li> * * <li><p>If <em>name</em> is equal to the string * "valueChangeListener" without the quotes, call {@link * javax.faces.component.EditableValueHolder#addValueChangeListener} on * <em>target</em>, passing <em>attributeMethodExpression</em> wrapped in a * {@link javax.faces.event.MethodExpressionValueChangeListener}.</p></li> * * <li><p>Otherwise, assume that the <code>MethodExpression</code> * should be placed in the components attribute set. The runtme * must create the <code>MethodExpression</code> instance based on * the value of the "<code>method-signature</code>" * attribute.</p></li> * </ul> * * </li> * * </ul> * * <p>An implementation is provided that will throw * <code>UnsupportedOperationException</code>. A Faces implementation * compliant with version 2.0 and beyond of the specification must * override this method.</p> * * </div> * * @param context the FacesContext for this request. * * @param topLevelComponent The UIComponent in the view to which the * attached objects must be attached. This UIComponent must have * its component metadata already associated and available from via * the JavaBeans API. * * @throws NullPointerException if <code>context</code> * or <code>topLevelComponent</code> is <code>null</code>. * * @since 2.0 */ public void retargetMethodExpressions(FacesContext context, UIComponent topLevelComponent) { // no-op } /** * <p class="changed_added_2_2">Return the list of resource library * contracts that will be made available for use in the view * specified by the argument {@code viewId}. If no match is found, * return an empty list. See section JSF.7.7.2 for the * specification of the default implementation. For backward * compatibility with prior implementations, an implementation is * provided that returns {@code null}, but any implementation * compliant with the version of the specification in which this * method was introduced must implement it as specified in * JSF.7.7.2. </p> * * @param context the {@code FacesContext} for this request * @param viewId the view id for which the applicable resource library * contracts should be calculated. * * @since 2.2 * * @return the calculated list of resource library contract names */ public List<String> calculateResourceLibraryContracts(FacesContext context, String viewId) { return null; } /** * <p class="changed_added_2_0">For implementations that want to * control the implementation of state saving and restoring, the * {@link StateManagementStrategy} allows them to do so. Returning * <code>null</code> indicates that the implementation wishes the * runtime to handle the state saving and restoring. * Implementations that provide the VDL for Facelets for JSF 2.0 and * later must return non-<code>null</code> from this method.</p> * * @param context the {@code FacesContext} for the current request. * * @param viewId the view id. * * @return the strategy as specified above * * @since 2.0 */ public abstract StateManagementStrategy getStateManagementStrategy(FacesContext context, String viewId); /** * <p class="changed_added_2_1"><span class="changed_modified_2_2">Tests</span> * whether a physical resource * corresponding to the specified viewId exists.</p> * * <p class="changed_modified_2_2">The default implementation uses * {@link javax.faces.application.ResourceHandler#createViewResource} * to locate the physical resource.</p> * * @param context The <code>FacesContext</code> for this request. * @param viewId the view id to test * * @return the result as specified above * * @since 2.1 */ public boolean viewExists(FacesContext context, String viewId) { return context.getApplication().getResourceHandler().createViewResource(context, viewId) != null; } /** * <p class="changed_added_2_3"> * Return a {@code Stream} possibly lazily populated by walking the view tree * rooted at a given initial path. The view tree is traversed <em>breadth-first</em>, * the elements in the stream are <em>logical</em> view ids. * </p> * * <p> * This method works as if invoking it were equivalent to evaluating the expression: * <blockquote><pre> * getViewResources(facesContext, start, Integer.MAX_VALUE, options) * </pre></blockquote> * Put differently, it visits all levels of the resource tree. * * @param facesContext The {@link FacesContext} for this request. * @param path The initial path from which to start looking for views * @param options The options to influence the traversal. See {@link ViewVisitOption} for details on those. * * @return the {@link Stream} of view ids * * @since 2.3 */ public Stream<String> getViews(FacesContext facesContext, String path, ViewVisitOption... options) { return facesContext.getApplication().getResourceHandler().getViewResources(facesContext, path, TOP_LEVEL_VIEWS_ONLY); } /** * <p class="changed_added_2_3"> * Return a {@code Stream} possibly lazily populated by walking the view tree * rooted at a given initial path. The view tree is traversed <em>breadth-first</em>, * the elements in the stream are <em>logical</em> view ids. * </p> * * <p> * The {@code maxDepth} parameter is the maximum depth of directory levels to visit * <em>beyond the initial path</em>, which is always visited. The value is relative to the root * ({@code /}), not to the given initial path. E.g. given {@code maxDepth} = {@code 3} and initial * path {@code /foo/}, visiting will proceed up to {@code /foo/bar/}, where {@code /} counts as depth * {@code 1}, {@code /foo/} as depth {@code 2} and {@code /foo/bar/} as depth {@code 3}. * A value lower or equal to the depth of the initial path means that only the initial path * is visited. A value of {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all * levels should be visited. * * @param facesContext The {@link FacesContext} for this request. * @param path The initial path from which to start looking for views * @param maxDepth The absolute maximum depth of nested directories to visit counted from the root ({@code /}). * @param options The options to influence the traversal. See {@link ViewVisitOption} for details on those. * * @return the {@link Stream} of view ids * * @since 2.3 */ public Stream<String> getViews(FacesContext facesContext, String path, int maxDepth, ViewVisitOption... options) { return facesContext.getApplication().getResourceHandler().getViewResources(facesContext, path, maxDepth, TOP_LEVEL_VIEWS_ONLY); } /** * <p class="changed_added_2_1">Returns a non-null String that can be * used to identify this view declaration language.</p> * * <p>The default implementation returns the fully qualified class name * of the view declaration language implementation. Subclasses may * override to provide a more meaningful id.</p> * * @return the id of this view declaration language * * @since 2.1 */ public String getId() { return getClass().getName(); } }