2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to you under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * $Id: CreateComponentCommand.java 516834 2007-03-11 01:33:36Z gvanmatre $
20   */
21  package org.apache.shale.clay.component.chain;
22  
23  import java.util.Iterator;
24  import java.util.Map;
25  
26  import javax.faces.component.UIComponent;
27  import javax.faces.context.FacesContext;
28  import javax.faces.el.ValueBinding;
29  
30  import org.apache.commons.chain.Context;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.shale.clay.component.Clay;
34  import org.apache.shale.clay.config.Globals;
35  import org.apache.shale.clay.config.beans.AttributeBean;
36  import org.apache.shale.clay.config.beans.Attributes;
37  import org.apache.shale.clay.config.beans.ComponentBean;
38  
39  /***
40   * <p>
41   * This <code>Command</code> is used to build <strong>parent</strong> and
42   * <strong>child</strong> <code>UIComponent</code>'s from the
43   * <code>displayElement</code> in the {@link org.apache.shale.clay.component.chain.ClayContext}.
44   * </p>
45   */
46  public class CreateComponentCommand extends AbstractCommand {
47  
48      /***
49       * <p>
50       * Common Logger utility.
51       * </p>
52       */
53      private static Log log;
54      static {
55          log = LogFactory.getLog(CreateComponentCommand.class);
56      }
57  
58  
59      /***
60       * <p>
61       * Creates a new faces component from the component metadata.
62       * </p>
63       * @param context common chains
64       * @return <code>true</code> if the chain is complete
65       * @exception Exception propagated up to the top of the chain
66       */
67      public boolean execute(Context context) throws Exception {
68  
69          boolean isFinal = true;
70  
71          ClayContext clayContext = (ClayContext) context;
72          if (clayContext == null) {
73              throw new NullPointerException(getMessages()
74                      .getMessage("clay.null.clayContext"));
75          }
76  
77          ComponentBean displayElement = clayContext.getDisplayElement();
78          if (displayElement == null) {
79              throw new NullPointerException(getMessages()
80                      .getMessage("clay.null.componentBean"));
81          }
82  
83          UIComponent parent = clayContext.getParent();
84          if (parent == null) {
85              throw new NullPointerException(getMessages()
86                      .getMessage("clay.null.parentBean"));
87          }
88  
89          FacesContext facesContext = clayContext.getFacesContext();
90          if (facesContext == null) {
91              throw new NullPointerException(getMessages()
92                      .getMessage("clay.null.facesContext"));
93          }
94  
95          // create a new scoped symbol table
96          Map symbolTable = new Attributes();
97          // inherit the parents symbols
98          symbolTable.putAll(clayContext.getSymbols());
99          // override config (XML, HTML) symbols
100         symbolTable.putAll(displayElement.getSymbols());
101         // push to context
102         clayContext.setSymbols(symbolTable);
103 
104         // evaluate nested symbols; symbols having symbols as values
105         realizeSymbols(clayContext);
106 
107         UIComponent child = null;
108         Long jspId = new Long(displayElement.getJspId());
109         clayContext.getJspIds().add(jspId);
110 
111         String facetName = displayElement.getFacetName();
112         if (facetName != null) {
113             facetName = replaceMnemonic(clayContext, facetName);
114         }
115         if (facetName != null) {
116             child = parent.getFacet(displayElement.getFacetName());
117         }
118         if (child == null) {
119            child = this.findComponentByJspId(parent, jspId);
120         }
121         // always burn an id; this is to support the early versions of myfaces & RI 1.1
122         // the UIViewRoot didn't persist the sequence that is used by the call to createUniqueId
123         String id = facesContext.getViewRoot().createUniqueId();
124         if (child == null) {
125             if (displayElement.getId() != null) {
126                 id = replaceMnemonic(clayContext, displayElement.getId());
127             }
128         } else {
129             id = child.getId();
130         }
131 
132         //Check to see if the replacement failed.  This can happen if the
133         //symbol is missing.  The id will still containing the symbol delimiter
134         //character.  The other scenario is if the value of the symbol is null.
135 
136         if (id == null || id.indexOf('@') > -1) {
137 
138            if (id == null) {
139               id = displayElement.getId();
140            }
141 
142            throw new RuntimeException(getMessages().getMessage("create.component.invalid.id",
143                    new Object[] {id, symbolTable}));
144         }
145 
146         if (child == null) {
147             try {
148                 AttributeBean attr = displayElement.getAttribute("binding");
149                 if (attr != null
150                     && attr.getValue() != null) {
151 
152                    clayContext.setAttribute(attr);
153                    String expr = replaceMnemonic(clayContext);
154                    ValueBinding vb = facesContext.getApplication().createValueBinding(expr);
155                    child = facesContext.getApplication().createComponent(vb, facesContext,
156                                                                     displayElement.getComponentType());
157                    child.setValueBinding("binding", vb);
158 
159                 } else {
160 
161                    child = facesContext.getApplication().createComponent(
162                         displayElement.getComponentType());
163                 }
164 
165             } catch (Exception e) {
166                 log.error(getMessages().getMessage("create.component.error",
167                         new Object[] { displayElement }), e);
168                         throw e;
169             }
170 
171             child.setId(id);
172             child.getAttributes().put(Globals.CLAY_JSPID_ATTRIBUTE, jspId);
173             if (facetName != null) {
174                 parent.getFacets().put(facetName, child);
175 
176                 if (log.isDebugEnabled()) {
177                     log.debug(getMessages().getMessage("create.facet.component",
178                         new Object[] { id, displayElement.getJsfid()}));
179                 }
180 
181             } else {
182                 parent.getChildren().add(clayContext.getChildIndex(), child);
183 
184                 if (log.isDebugEnabled()) {
185                     log.debug(getMessages().getMessage("create.component",
186                         new Object[] { id, displayElement.getJsfid(), new Integer(clayContext.getChildIndex()) }));
187                 }
188 
189             }
190 
191             if (child instanceof Clay) {
192                 //save the display element used to create the component for exception
193                 //reporting when the jsfid is null.  This should only happen when using
194                 //symbol replacement of a nested clay component with HTML views
195                 child.getAttributes().put(Globals.CLAY_RESERVED_ATTRIBUTE, displayElement);
196             }
197 
198             // continue with the addComponent chain
199             isFinal = false;
200         } else {
201             if (log.isDebugEnabled()) {
202                 log.debug(getMessages().getMessage("create.component.exists",
203                     new Object[] { id, displayElement.getJsfid(), new Integer(clayContext.getChildIndex()) }));
204             }
205         }
206 
207         // if target is a Clay component it might contain symbols
208         if (child instanceof Clay) {
209             // override symbols from nested clay component
210             symbolTable.putAll(((Clay) child).getSymbols());
211             // capture current symbols for the root of the nested component
212             ((Clay) child).getSymbols().putAll(symbolTable);
213 
214             // push to context
215             clayContext.setSymbols(symbolTable);
216 
217             // evaluate nested symbols
218             realizeSymbols(clayContext);
219         }
220 
221 
222         // reassign the child to the converter for the
223         // AssignPropertiesCommand
224         clayContext.setChild(child);
225 
226         return isFinal;
227     }
228 
229     /***
230      * <p>Searches through the parent's children looking for a child
231      * component having a matching "org.apache.shale.clay.jspid" attribute.
232      * </p>
233      * @param parent owning <code>UIComponent</code>
234      * @param id target jsfid
235      * @return the child <code>UIComponent</code> if a match is found; otherwise;
236      *   a <code>null</code> value.
237      */
238     private UIComponent findComponentByJspId(UIComponent parent, Long id) {
239         Iterator ci = parent.getChildren().iterator();
240         while (ci.hasNext()) {
241            UIComponent child = (UIComponent) ci.next();
242            Long jspId = (Long) child.getAttributes().get(Globals.CLAY_JSPID_ATTRIBUTE);
243            if (jspId != null && jspId.equals(id)) {
244               return child;
245            }
246         }
247         return null;
248     }
249 }