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.buildtools.maven2.plugin.builder.annotation.JSFFaceletAttribute;
40  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
41  import org.apache.myfaces.shared.util.ClassUtils;
42  import org.apache.myfaces.view.facelets.AbstractFaceletContext;
43  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
44  import org.apache.myfaces.view.facelets.el.VariableMapperWrapper;
45  import org.apache.myfaces.view.facelets.impl.TemplateContextImpl;
46  import org.apache.myfaces.view.facelets.tag.ComponentContainerHandler;
47  import org.apache.myfaces.view.facelets.tag.TagHandlerUtils;
48  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
49  
50  /**
51   * The include tag can point at any Facelet which might use the composition tag,
52   * component tag, or simply be straight XHTML/XML. It should be noted that the 
53   * src path does allow relative path names, but they will always be resolved 
54   * against the original Facelet requested. 
55   * 
56   * The include tag can be used in conjunction with multiple <ui:param/> 
57   * tags to pass EL expressions/values to the target page.
58   * 
59   * @author Jacob Hookom
60   * @version $Id$
61   */
62  @JSFFaceletTag(name="ui:include", bodyContent="JSP")
63  public final class IncludeHandler extends TagHandler implements ComponentContainerHandler
64  {
65  
66      private static final String ERROR_PAGE_INCLUDE_PATH = "javax.faces.error.xhtml";
67      private static final String ERROR_FACELET = "META-INF/rsc/myfaces-dev-error-include.xhtml";
68      
69      /**
70       * A literal or EL expression that specifies the target Facelet that you 
71       * would like to include into your document.
72       */
73      @JSFFaceletAttribute(
74              className="javax.el.ValueExpression",
75              deferredValueType="java.lang.String",
76              required=true)
77      private final TagAttribute src;
78      
79      private final ParamHandler[] _params;
80  
81      /**
82       * @param config
83       */
84      public IncludeHandler(TagConfig config)
85      {
86          super(config);
87          this.src = this.getRequiredAttribute("src");
88          
89          Collection<ParamHandler> params = TagHandlerUtils.findNextByType(nextHandler, ParamHandler.class);
90          if (!params.isEmpty())
91          {
92              int i = 0;
93              _params = new ParamHandler[params.size()];
94              for (ParamHandler handler : params)
95              {
96                  _params[i++] = handler;
97              }
98          }
99          else
100         {
101             _params = null;
102         }
103     }
104 
105     /*
106      * (non-Javadoc)
107      * 
108      * @see javax.faces.view.facelets.FaceletHandler#apply(javax.faces.view.facelets.FaceletContext, javax.faces.component.UIComponent)
109      */
110     public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException,
111             ELException
112     {
113         AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
114         FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(ctx);
115         String path;
116         boolean markInitialState = false;
117         String uniqueId = null;
118         if (!src.isLiteral())
119         {
120             uniqueId = actx.generateUniqueFaceletTagId(
121                 fcc.startComponentUniqueIdSection(), tagId);
122         }
123         else if (_params != null)
124         {
125             uniqueId = actx.generateUniqueFaceletTagId(
126                 fcc.generateUniqueComponentId(), tagId);
127         }
128         if (!src.isLiteral())
129         {
130             //String uniqueId = fcc.startComponentUniqueIdSection();
131             //path = getSrcValue(actx, fcc, parent, uniqueId);
132             String restoredPath = (String) ComponentSupport.restoreInitialTagState(ctx, fcc, parent, uniqueId);
133             if (restoredPath != null)
134             {
135                 // If is not restore view phase, the path value should be
136                 // evaluated and if is not equals, trigger markInitialState stuff.
137                 if (!PhaseId.RESTORE_VIEW.equals(ctx.getFacesContext().getCurrentPhaseId()))
138                 {
139                     path = this.src.getValue(ctx);
140                     if (path == null || path.length() == 0)
141                     {
142                         return;
143                     }
144                     if (!path.equals(restoredPath))
145                     {
146                         markInitialState = true;
147                     }
148                 }
149                 else
150                 {
151                     path = restoredPath;
152                 }
153             }
154             else
155             {
156                 //No state restored, calculate path
157                 path = this.src.getValue(ctx);
158             }
159             ComponentSupport.saveInitialTagState(ctx, fcc, parent, uniqueId, path);
160         }
161         else
162         {
163             path = this.src.getValue(ctx);
164         }
165         try
166         {
167             if (path == null || path.length() == 0)
168             {
169                 return;
170             }
171             VariableMapper orig = ctx.getVariableMapper();
172             ctx.setVariableMapper(new VariableMapperWrapper(orig));
173             try
174             {
175                 //Only ui:param could be inside ui:include.
176                 //this.nextHandler.apply(ctx, null);
177                 
178                 URL url = null;
179                 boolean oldMarkInitialState = false;
180                 Boolean isBuildingInitialState = null;
181                 // if we are in ProjectStage Development and the path equals "javax.faces.error.xhtml"
182                 // we should include the default error page
183                 if (ctx.getFacesContext().isProjectStage(ProjectStage.Development) 
184                         && ERROR_PAGE_INCLUDE_PATH.equals(path))
185                 {
186                     url =ClassUtils.getResource(ERROR_FACELET);
187                 }
188                 if (markInitialState)
189                 {
190                     //set markInitialState flag
191                     oldMarkInitialState = fcc.isMarkInitialState();
192                     fcc.setMarkInitialState(true);
193                     isBuildingInitialState = (Boolean) ctx.getFacesContext().getAttributes().put(
194                             StateManager.IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
195                 }
196                 try
197                 {
198                     if (_params != null)
199                     {
200                         // ui:include defines a new TemplateContext, but ui:param EL expressions
201                         // defined inside should be built before the new context is setup, to
202                         // apply then after. The final effect is EL expressions will be resolved
203                         // correctly when nested ui:params with the same name or based on other
204                         // ui:params are used.
205                         
206                         String[] names = new String[_params.length];
207                         ValueExpression[] values = new ValueExpression[_params.length];
208                         
209                         for (int i = 0; i < _params.length; i++)
210                         {
211                             names[i] = _params[i].getName(ctx);
212                             values[i] = _params[i].getValue(ctx);
213                         }
214                         
215                         actx.pushTemplateContext(new TemplateContextImpl());
216                         
217                         for (int i = 0; i < _params.length; i++)
218                         {
219                             _params[i].apply(ctx, parent, names[i], values[i], uniqueId);
220                         }
221                     }
222                     else
223                     {
224                         actx.pushTemplateContext(new TemplateContextImpl());
225                     }
226                     if (url == null)
227                     {
228                         ctx.includeFacelet(parent, path);
229                     }
230                     else
231                     {
232                         ctx.includeFacelet(parent, url);
233                     }
234                 }
235                 finally
236                 {
237                     if (markInitialState)
238                     {
239                         //unset markInitialState flag
240                         if (isBuildingInitialState == null)
241                         {
242                             ctx.getFacesContext().getAttributes().remove(
243                                     StateManager.IS_BUILDING_INITIAL_STATE);
244                         }
245                         else
246                         {
247                             ctx.getFacesContext().getAttributes().put(
248                                     StateManager.IS_BUILDING_INITIAL_STATE, isBuildingInitialState);
249                         }
250                         fcc.setMarkInitialState(oldMarkInitialState);
251                     }
252                     actx.popTemplateContext();
253                 }
254             }
255             finally
256             {
257                 ctx.setVariableMapper(orig);
258             }
259         }
260         finally
261         {
262             if (!src.isLiteral())
263             {
264                 fcc.endComponentUniqueIdSection();
265                 
266                 if (fcc.isUsingPSSOnThisView() && fcc.isRefreshTransientBuildOnPSS() &&
267                     !fcc.isRefreshingTransientBuild())
268                 {
269                     //Mark the parent component to be saved and restored fully.
270                     ComponentSupport.markComponentToRestoreFully(ctx.getFacesContext(), parent);
271                 }
272                 if (fcc.isDynamicComponentSection())
273                 {
274                     ComponentSupport.markComponentToRefreshDynamically(ctx.getFacesContext(), parent);
275                 }
276             }
277         }
278     }
279 }