View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.view.facelets.tag.ui;
20  
21  import java.io.IOException;
22  import java.net.URL;
23  import java.util.Collection;
24  
25  import javax.el.ELException;
26  import javax.el.ValueExpression;
27  import javax.el.VariableMapper;
28  import javax.faces.FacesException;
29  import javax.faces.application.ProjectStage;
30  import javax.faces.application.StateManager;
31  import javax.faces.component.UIComponent;
32  import javax.faces.event.PhaseId;
33  import javax.faces.view.facelets.FaceletContext;
34  import javax.faces.view.facelets.FaceletException;
35  import javax.faces.view.facelets.TagAttribute;
36  import javax.faces.view.facelets.TagConfig;
37  import javax.faces.view.facelets.TagHandler;
38  
39  import org.apache.myfaces.shared.util.ClassUtils;
40  import org.apache.myfaces.view.facelets.AbstractFaceletContext;
41  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
42  import org.apache.myfaces.view.facelets.el.VariableMapperWrapper;
43  import org.apache.myfaces.view.facelets.tag.ComponentContainerHandler;
44  import org.apache.myfaces.view.facelets.tag.TagHandlerUtils;
45  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
46  
47  /**
48   * The include tag can point at any Facelet which might use the composition tag,
49   * component tag, or simply be straight XHTML/XML. It should be noted that the 
50   * src path does allow relative path names, but they will always be resolved 
51   * against the original Facelet requested. 
52   * 
53   * The include tag can be used in conjunction with multiple <ui:param/> 
54   * tags to pass EL expressions/values to the target page.
55   * 
56   * NOTE: This implementation is provided for compatibility reasons and
57   * it is considered faulty. It is enabled using
58   * org.apache.myfaces.STRICT_JSF_2_FACELETS_COMPATIBILITY web config param.
59   * Don't use it if EL expression caching is enabled.
60   * 
61   * @author Jacob Hookom
62   * @version $Id$
63   */
64  //@JSFFaceletTag(name="ui:include", bodyContent="JSP")
65  public final class LegacyIncludeHandler extends TagHandler implements ComponentContainerHandler
66  {
67  
68      private static final String ERROR_PAGE_INCLUDE_PATH = "javax.faces.error.xhtml";
69      private static final String ERROR_FACELET = "META-INF/rsc/myfaces-dev-error-include.xhtml";
70      
71      /**
72       * A literal or EL expression that specifies the target Facelet that you 
73       * would like to include into your document.
74       */
75      //@JSFFaceletAttribute(
76      //        className="javax.el.ValueExpression",
77      //        deferredValueType="java.lang.String",
78      //        required=true)
79      private final TagAttribute src;
80      
81      private final LegacyParamHandler[] _params;
82  
83      /**
84       * @param config
85       */
86      public LegacyIncludeHandler(TagConfig config)
87      {
88          super(config);
89          this.src = this.getRequiredAttribute("src");
90          
91          Collection<LegacyParamHandler> params = TagHandlerUtils.findNextByType(nextHandler, 
92                  LegacyParamHandler.class);
93          if (!params.isEmpty())
94          {
95              int i = 0;
96              _params = new LegacyParamHandler[params.size()];
97              for (LegacyParamHandler handler : params)
98              {
99                  _params[i++] = handler;
100             }
101         }
102         else
103         {
104             _params = null;
105         }
106     }
107 
108     /*
109      * (non-Javadoc)
110      * 
111      * @see javax.faces.view.facelets.FaceletHandler#apply(javax.faces.view.facelets.FaceletContext, javax.faces.component.UIComponent)
112      */
113     public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException,
114             ELException
115     {
116         AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
117         FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(ctx);
118         String path;
119         boolean markInitialState = false;
120         if (!src.isLiteral())
121         {
122             String uniqueId = actx.generateUniqueFaceletTagId(fcc.startComponentUniqueIdSection(), tagId);
123             //path = getSrcValue(actx, fcc, parent, uniqueId);
124             String restoredPath = (String) ComponentSupport.restoreInitialTagState(ctx, fcc, parent, uniqueId);
125             if (restoredPath != null)
126             {
127                 // If is not restore view phase, the path value should be
128                 // evaluated and if is not equals, trigger markInitialState stuff.
129                 if (!PhaseId.RESTORE_VIEW.equals(ctx.getFacesContext().getCurrentPhaseId()))
130                 {
131                     path = this.src.getValue(ctx);
132                     if (path == null || path.length() == 0)
133                     {
134                         return;
135                     }
136                     if (!path.equals(restoredPath))
137                     {
138                         markInitialState = true;
139                     }
140                 }
141                 else
142                 {
143                     path = restoredPath;
144                 }
145             }
146             else
147             {
148                 //No state restored, calculate path
149                 path = this.src.getValue(ctx);
150             }
151             ComponentSupport.saveInitialTagState(ctx, fcc, parent, uniqueId, path);
152         }
153         else
154         {
155             path = this.src.getValue(ctx);
156         }
157         try
158         {
159             if (path == null || path.length() == 0)
160             {
161                 return;
162             }
163             VariableMapper orig = ctx.getVariableMapper();
164             ctx.setVariableMapper(new VariableMapperWrapper(orig));
165             try
166             {
167                 //Only ui:param could be inside ui:include.
168                 //this.nextHandler.apply(ctx, null);
169                 
170                 URL url = null;
171                 boolean oldMarkInitialState = false;
172                 Boolean isBuildingInitialState = null;
173                 // if we are in ProjectStage Development and the path equals "javax.faces.error.xhtml"
174                 // we should include the default error page
175                 if (ctx.getFacesContext().isProjectStage(ProjectStage.Development) 
176                         && ERROR_PAGE_INCLUDE_PATH.equals(path))
177                 {
178                     url =ClassUtils.getResource(ERROR_FACELET);
179                 }
180                 if (markInitialState)
181                 {
182                     //set markInitialState flag
183                     oldMarkInitialState = fcc.isMarkInitialState();
184                     fcc.setMarkInitialState(true);
185                     isBuildingInitialState = (Boolean) ctx.getFacesContext().getAttributes().put(
186                             StateManager.IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
187                 }
188                 try
189                 {
190                     if (_params != null)
191                     {
192                         // ui:include defines a new TemplateContext, but ui:param EL expressions
193                         // defined inside should be built before the new context is setup, to
194                         // apply then after. The final effect is EL expressions will be resolved
195                         // correctly when nested ui:params with the same name or based on other
196                         // ui:params are used.
197                         
198                         String[] names = new String[_params.length];
199                         ValueExpression[] values = new ValueExpression[_params.length];
200                         
201                         for (int i = 0; i < _params.length; i++)
202                         {
203                             names[i] = _params[i].getName(ctx);
204                             values[i] = _params[i].getValue(ctx);
205                         }
206                         
207                         //actx.pushTemplateContext(new TemplateContextImpl());
208                         
209                         for (int i = 0; i < _params.length; i++)
210                         {
211                             _params[i].apply(ctx, parent, names[i], values[i]);
212                         }
213                     }
214                     else
215                     {
216                         //actx.pushTemplateContext(new TemplateContextImpl());
217                     }
218                     if (url == null)
219                     {
220                         ctx.includeFacelet(parent, path);
221                     }
222                     else
223                     {
224                         ctx.includeFacelet(parent, url);
225                     }
226                 }
227                 finally
228                 {
229                     if (markInitialState)
230                     {
231                         //unset markInitialState flag
232                         if (isBuildingInitialState == null)
233                         {
234                             ctx.getFacesContext().getAttributes().remove(
235                                     StateManager.IS_BUILDING_INITIAL_STATE);
236                         }
237                         else
238                         {
239                             ctx.getFacesContext().getAttributes().put(
240                                     StateManager.IS_BUILDING_INITIAL_STATE, isBuildingInitialState);
241                         }
242                         fcc.setMarkInitialState(oldMarkInitialState);
243                     }
244                     //actx.popTemplateContext();
245                 }
246             }
247             finally
248             {
249                 ctx.setVariableMapper(orig);
250             }
251         }
252         finally
253         {
254             if (!src.isLiteral())
255             {
256                 fcc.endComponentUniqueIdSection();
257                 
258                 if (fcc.isUsingPSSOnThisView() && fcc.isRefreshTransientBuildOnPSS() &&
259                     !fcc.isRefreshingTransientBuild())
260                 {
261                     //Mark the parent component to be saved and restored fully.
262                     ComponentSupport.markComponentToRestoreFully(ctx.getFacesContext(), parent);
263                 }
264                 if (fcc.isDynamicComponentSection())
265                 {
266                     ComponentSupport.markComponentToRefreshDynamically(ctx.getFacesContext(), parent);
267                 }
268             }
269         }
270     }
271 }