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     @Override
111     public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, FaceletException,
112             ELException
113     {
114         AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
115         FaceletCompositionContext fcc = FaceletCompositionContext.getCurrentInstance(ctx);
116         String path;
117         boolean markInitialState = false;
118         String uniqueId = null;
119         if (!src.isLiteral())
120         {
121             uniqueId = actx.generateUniqueFaceletTagId(
122                 fcc.startComponentUniqueIdSection(), tagId);
123         }
124         else if (_params != null)
125         {
126             uniqueId = actx.generateUniqueFaceletTagId(
127                 fcc.generateUniqueComponentId(), tagId);
128         }
129         if (!src.isLiteral())
130         {
131             //String uniqueId = fcc.startComponentUniqueIdSection();
132             //path = getSrcValue(actx, fcc, parent, uniqueId);
133             String restoredPath = (String) ComponentSupport.restoreInitialTagState(ctx, fcc, parent, uniqueId);
134             if (restoredPath != null)
135             {
136                 // If is not restore view phase, the path value should be
137                 // evaluated and if is not equals, trigger markInitialState stuff.
138                 if (!PhaseId.RESTORE_VIEW.equals(ctx.getFacesContext().getCurrentPhaseId()))
139                 {
140                     path = this.src.getValue(ctx);
141                     if (path == null || path.length() == 0)
142                     {
143                         return;
144                     }
145                     if (!path.equals(restoredPath))
146                     {
147                         markInitialState = true;
148                     }
149                 }
150                 else
151                 {
152                     path = restoredPath;
153                 }
154             }
155             else
156             {
157                 //No state restored, calculate path
158                 path = this.src.getValue(ctx);
159             }
160             ComponentSupport.saveInitialTagState(ctx, fcc, parent, uniqueId, path);
161         }
162         else
163         {
164             path = this.src.getValue(ctx);
165         }
166         try
167         {
168             if (path == null || path.length() == 0)
169             {
170                 return;
171             }
172             VariableMapper orig = ctx.getVariableMapper();
173             ctx.setVariableMapper(new VariableMapperWrapper(orig));
174             try
175             {
176                 //Only ui:param could be inside ui:include.
177                 //this.nextHandler.apply(ctx, null);
178                 
179                 URL url = null;
180                 boolean oldMarkInitialState = false;
181                 Boolean isBuildingInitialState = null;
182                 // if we are in ProjectStage Development and the path equals "javax.faces.error.xhtml"
183                 // we should include the default error page
184                 if (ctx.getFacesContext().isProjectStage(ProjectStage.Development) 
185                         && ERROR_PAGE_INCLUDE_PATH.equals(path))
186                 {
187                     url =ClassUtils.getResource(ERROR_FACELET);
188                 }
189                 if (markInitialState)
190                 {
191                     //set markInitialState flag
192                     oldMarkInitialState = fcc.isMarkInitialState();
193                     fcc.setMarkInitialState(true);
194                     isBuildingInitialState = (Boolean) ctx.getFacesContext().getAttributes().put(
195                             StateManager.IS_BUILDING_INITIAL_STATE, Boolean.TRUE);
196                 }
197                 try
198                 {
199                     if (_params != null)
200                     {
201                         // ui:include defines a new TemplateContext, but ui:param EL expressions
202                         // defined inside should be built before the new context is setup, to
203                         // apply then after. The final effect is EL expressions will be resolved
204                         // correctly when nested ui:params with the same name or based on other
205                         // ui:params are used.
206                         
207                         String[] names = new String[_params.length];
208                         ValueExpression[] values = new ValueExpression[_params.length];
209                         
210                         for (int i = 0; i < _params.length; i++)
211                         {
212                             names[i] = _params[i].getName(ctx);
213                             values[i] = _params[i].getValue(ctx);
214                         }
215                         
216                         actx.pushTemplateContext(new TemplateContextImpl());
217                         
218                         for (int i = 0; i < _params.length; i++)
219                         {
220                             _params[i].apply(ctx, parent, names[i], values[i], uniqueId);
221                         }
222                     }
223                     else
224                     {
225                         actx.pushTemplateContext(new TemplateContextImpl());
226                     }
227                     if (url == null)
228                     {
229                         ctx.includeFacelet(parent, path);
230                     }
231                     else
232                     {
233                         ctx.includeFacelet(parent, url);
234                     }
235                 }
236                 finally
237                 {
238                     if (markInitialState)
239                     {
240                         //unset markInitialState flag
241                         if (isBuildingInitialState == null)
242                         {
243                             ctx.getFacesContext().getAttributes().remove(
244                                     StateManager.IS_BUILDING_INITIAL_STATE);
245                         }
246                         else
247                         {
248                             ctx.getFacesContext().getAttributes().put(
249                                     StateManager.IS_BUILDING_INITIAL_STATE, isBuildingInitialState);
250                         }
251                         fcc.setMarkInitialState(oldMarkInitialState);
252                     }
253                     actx.popTemplateContext();
254                 }
255             }
256             finally
257             {
258                 ctx.setVariableMapper(orig);
259             }
260         }
261         finally
262         {
263             if (!src.isLiteral())
264             {
265                 fcc.endComponentUniqueIdSection();
266                 
267                 if (fcc.isUsingPSSOnThisView() && fcc.isRefreshTransientBuildOnPSS() &&
268                     !fcc.isRefreshingTransientBuild())
269                 {
270                     //Mark the parent component to be saved and restored fully.
271                     ComponentSupport.markComponentToRestoreFully(ctx.getFacesContext(), parent);
272                 }
273                 if (fcc.isDynamicComponentSection())
274                 {
275                     ComponentSupport.markComponentToRefreshDynamically(ctx.getFacesContext(), parent);
276                 }
277             }
278         }
279     }
280 }