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.compiler;
20  
21  import java.io.IOException;
22  import java.util.Map;
23  import javax.faces.FacesException;
24  import javax.faces.component.StateHolder;
25  import javax.faces.component.UIComponent;
26  import javax.faces.context.FacesContext;
27  import javax.faces.event.ComponentSystemEvent;
28  import javax.faces.event.ComponentSystemEventListener;
29  import javax.faces.view.facelets.Facelet;
30  import org.apache.myfaces.config.RuntimeConfig;
31  import org.apache.myfaces.view.facelets.AbstractFacelet;
32  import org.apache.myfaces.view.facelets.FaceletFactory;
33  import org.apache.myfaces.view.facelets.FaceletViewDeclarationLanguage;
34  import org.apache.myfaces.view.facelets.tag.composite.CompositeResourceLibrary;
35  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
36  
37  /**
38   * This listener must be attached to PostRestoreStateEvent so when the view is restored, 
39   * the algorithm refresh the component that was created using facelets, restoring the
40   * transient markup.
41   *
42   * @author lu4242
43   */
44  public final class RefreshDynamicComponentListener implements 
45      ComponentSystemEventListener, StateHolder
46  {
47      private String taglibURI;
48      private String tagName;
49      private Map<String,Object> attributes;
50      private String baseKey;
51  
52      public RefreshDynamicComponentListener(String taglibURI, String tagName, 
53          Map<String, Object> attributes, String baseKey)
54      {
55          this.taglibURI = taglibURI;
56          this.tagName = tagName;
57          this.attributes = attributes;
58          this.baseKey = baseKey;
59      }
60  
61      public RefreshDynamicComponentListener()
62      {
63      }
64      
65      public void processEvent(ComponentSystemEvent event)
66      {
67          FacesContext facesContext = FacesContext.getCurrentInstance();
68          //if (!PhaseId.RESTORE_VIEW.equals(facesContext.getCurrentPhaseId()))
69          //{
70              // This listener is only active when PostAddToViewEvent occur
71              // on restore view phase. 
72              //return;
73          //}
74          
75          FaceletViewDeclarationLanguage vdl = (FaceletViewDeclarationLanguage) 
76              facesContext.getApplication().getViewHandler().getViewDeclarationLanguage(
77                  facesContext, facesContext.getViewRoot().getViewId());
78          
79          Facelet componentFacelet;
80          FaceletFactory faceletFactory = vdl.getFaceletFactory();
81              
82          FaceletFactory.setInstance(faceletFactory);
83          try
84          {
85              componentFacelet
86                      = faceletFactory.compileComponentFacelet(taglibURI, tagName, attributes);
87          }
88          finally
89          {
90              FaceletFactory.setInstance(null);
91          }
92          try
93          {
94              UIComponent component = event.getComponent();
95              
96              facesContext.getAttributes().put(FaceletViewDeclarationLanguage.REFRESHING_TRANSIENT_BUILD,
97                  Boolean.TRUE);
98              
99              
100             // Detect the relationship between parent and child, to ensure the component is properly created
101             // and refreshed. In facelets this is usually done by core.FacetHandler, but since it is a 
102             // dynamic component, we need to do it here before apply the handler
103             UIComponent parent = component.getParent();
104             String facetName = null;
105             if (parent.getFacetCount() > 0 && !parent.getChildren().contains(component))
106             {
107                 facetName = ComponentSupport.findFacetNameByComponentInstance(parent, component);
108             }
109 
110             try
111             {
112                 if (facetName != null)
113                 {
114                     parent.getAttributes().put(org.apache.myfaces.view.facelets.tag.jsf.core.FacetHandler.KEY, 
115                             facetName);
116                 }            
117                 // The trick here is restore MARK_CREATED, just to allow ComponentTagHandlerDelegate to
118                 // find the component. Then we reset it to exclude it from facelets refresh algorithm.
119                 // Note oam.vf.GEN_MARK_ID helps to identify when there is a wrapper component or not,
120                 // and in that way identify which component is the parent.
121                 String markId = (String) component.getAttributes().get("oam.vf.GEN_MARK_ID");
122                 if (markId == null)
123                 {
124                     ((AbstractFacelet)componentFacelet).applyDynamicComponentHandler(
125                         facesContext, component, baseKey);
126                 }
127                 else
128                 {
129                     try
130                     {
131                         component.getAttributes().put(ComponentSupport.MARK_CREATED, markId);
132                         ((AbstractFacelet)componentFacelet).applyDynamicComponentHandler(
133                             facesContext, component.getParent(), baseKey);
134                     }
135                     finally
136                     {
137                         component.getAttributes().put(ComponentSupport.MARK_CREATED, null);
138                     }
139                 }
140             }
141             finally
142             {
143                 if (facetName != null)
144                 {
145                     parent.getAttributes().remove(org.apache.myfaces.view.facelets.tag.jsf.core.FacetHandler.KEY);
146                 }
147             }
148         }
149         catch (IOException e)
150         {
151             throw new FacesException(e);
152         }
153         finally
154         {
155             facesContext.getAttributes().remove(
156                 FaceletViewDeclarationLanguage.REFRESHING_TRANSIENT_BUILD);
157         }
158     }
159 
160     public Object saveState(FacesContext context)
161     {
162         RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(
163             context.getExternalContext());
164         Object[] values = new Object[4];
165         Integer tagId = runtimeConfig.getIdByNamespace().get(taglibURI);
166         if (tagId != null)
167         {
168             values[0] = tagId;
169         }
170         else if (taglibURI.startsWith(CompositeResourceLibrary.NAMESPACE_PREFIX))
171         {
172             values[0] = new Object[]{0, taglibURI.substring(35)};
173         }
174         else if(taglibURI.startsWith(CompositeResourceLibrary.ALIAS_NAMESPACE_PREFIX))
175         {
176             values[0] = new Object[]{1, taglibURI.substring(34)};
177         }
178         else
179         {
180             values[0] = taglibURI;
181         }
182         values[1] = tagName;
183         values[2] = attributes;
184         values[3] = baseKey;
185         return values;
186     }
187 
188     public void restoreState(FacesContext context, Object state)
189     {
190         Object[] values = (Object[]) state;
191         if (values[0] instanceof String)
192         {
193             taglibURI = (String) values[0];
194         }
195         else if (values[0] instanceof Integer)
196         {
197             RuntimeConfig runtimeConfig = RuntimeConfig.getCurrentInstance(
198                 context.getExternalContext());
199             taglibURI = runtimeConfig.getNamespaceById().get((Integer)values[0]);
200         }
201         else if (values[0] instanceof Object[])
202         {
203             Object[] def = (Object[])values[0];
204             String ns = ( ((Integer)def[0]).intValue() == 0) ? 
205                 CompositeResourceLibrary.NAMESPACE_PREFIX :
206                 CompositeResourceLibrary.ALIAS_NAMESPACE_PREFIX;
207             taglibURI = ns + (String)(((Object[])values[0])[1]);
208         }
209         tagName = (String)values[1];
210         attributes = (Map<String,Object>) values[2];
211         baseKey = (String)values[3];
212     }
213 
214     public boolean isTransient()
215     {
216         return false;
217     }
218 
219     public void setTransient(boolean newTransientValue)
220     {
221     }
222 
223 }