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  
20  package org.apache.myfaces.tobago.facelets.extension;
21  
22  import org.apache.myfaces.tobago.component.Attributes;
23  import org.apache.myfaces.tobago.component.Facets;
24  import org.apache.myfaces.tobago.component.InputSuggest;
25  import org.apache.myfaces.tobago.component.OnComponentCreated;
26  import org.apache.myfaces.tobago.component.OnComponentPopulated;
27  import org.apache.myfaces.tobago.component.RendererTypes;
28  import org.apache.myfaces.tobago.component.SupportsMarkup;
29  import org.apache.myfaces.tobago.component.UIGridLayout;
30  import org.apache.myfaces.tobago.component.UILabel;
31  import org.apache.myfaces.tobago.component.UIPanel;
32  import org.apache.myfaces.tobago.context.Markup;
33  import org.apache.myfaces.tobago.facelets.SuggestMethodRule;
34  import org.apache.myfaces.tobago.facelets.SupportsMarkupRule;
35  import org.apache.myfaces.tobago.facelets.TobagoComponentHandler;
36  import org.apache.myfaces.tobago.internal.layout.LayoutUtils;
37  import org.apache.myfaces.tobago.util.ComponentUtils;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  import javax.el.ELException;
42  import javax.el.ValueExpression;
43  import javax.faces.FacesException;
44  import javax.faces.application.Application;
45  import javax.faces.component.EditableValueHolder;
46  import javax.faces.component.UIComponent;
47  import javax.faces.component.UIViewRoot;
48  import javax.faces.view.facelets.ComponentConfig;
49  import javax.faces.view.facelets.ComponentHandler;
50  import javax.faces.view.facelets.FaceletContext;
51  import javax.faces.view.facelets.MetaRuleset;
52  import javax.faces.view.facelets.Metadata;
53  import javax.faces.view.facelets.TagAttribute;
54  import java.io.IOException;
55  
56  public abstract class TobagoLabelExtensionHandler extends ComponentHandler {
57    private static final Logger LOG = LoggerFactory.getLogger(TobagoLabelExtensionHandler.class);
58    private static final String DEFAULT_COLUMNS = "auto;*";
59    private TagAttribute labelWidthAttribute;
60    private TagAttribute tipAttribute;
61    private TagAttribute labelAttribute;
62    private TagAttribute markupAttribute;
63    private TagAttribute fieldIdAttribute;
64    private Class subComponentLastType = Object.class;
65    private Metadata subComponentMapper;
66  
67    public TobagoLabelExtensionHandler(ComponentConfig config) {
68      super(config);
69      labelWidthAttribute = getAttribute("labelWidth");
70      tipAttribute = getAttribute(Attributes.TIP);
71      labelAttribute = getAttribute(Attributes.LABEL);
72      markupAttribute = getAttribute(Attributes.MARKUP);
73      fieldIdAttribute = getAttribute("fieldId");
74    }
75  
76    protected abstract String getSubComponentType();
77  
78    protected abstract String getSubRendererType();
79  
80    protected String getRows() {
81      return "auto";
82    }
83  
84    protected String getColumns(String first) {
85      return first + ";*";
86    }
87  
88    public void applyNextHandler(FaceletContext ctx, UIComponent panel)
89        throws IOException, FacesException, ELException {
90      if (ComponentHandler.isNew(panel)) {
91        // ensure that input has no parent (isNew)
92        UIComponent input  = panel.getChildren().remove(1);
93        try {
94          input.getAttributes().put("tobago.panel", panel);
95          nextHandler.apply(ctx, input);
96        } finally {
97          input.getAttributes().remove("tobago.panel");
98        }
99        UIComponent date = null;
100       if (panel.getChildCount() > 1) {
101         date = panel.getChildren().get(1);
102       }
103       panel.getChildren().add(input);
104       if (date != null) {
105         panel.getChildren().add(date);
106       }
107     } else {
108       UIComponent input = panel.getChildren().get(1);
109       nextHandler.apply(ctx, input);
110     }
111   }
112 
113   public void onComponentCreated(FaceletContext faceletContext, UIComponent panel, UIComponent parent) {
114 
115     Application application = faceletContext.getFacesContext().getApplication();
116     UIViewRoot root = ComponentUtils.findViewRoot(faceletContext, parent);
117 
118     addGridLayout(faceletContext, panel, root);
119 
120     addLabel(faceletContext, (UIPanel) panel, root);
121     String uid;
122     if (fieldIdAttribute !=  null) {
123       uid = fieldIdAttribute.getValue(faceletContext);
124     } else {
125       uid = root.createUniqueId();
126     }
127     if (checkForAlreadyCreated(panel, uid)) {
128       return;
129     }
130 
131     UIComponent input = application.createComponent(getSubComponentType());
132     input.setRendererType(getSubRendererType());
133     input.setId(uid);
134 
135     setSubComponentAttributes(faceletContext, input);
136     enrichInput(faceletContext, input);
137 
138     panel.getChildren().add(input);
139   }
140 
141   protected void enrichInput(FaceletContext faceletContext, UIComponent input) {
142   }
143 
144   private void addLabel(FaceletContext faceletContext, UIPanel panel, UIViewRoot root) {
145     String uid = root.createUniqueId();
146     if (checkForAlreadyCreated(panel, uid)) {
147       return;
148     }
149     Application application = faceletContext.getFacesContext().getApplication();
150     UILabel label = (UILabel) application.createComponent(UILabel.COMPONENT_TYPE);
151     label.setRendererType(RendererTypes.LABEL);
152     label.setId(uid);
153     label.getAttributes().put(Attributes.FOR, "@auto");
154     if (tipAttribute != null) {
155       if (tipAttribute.isLiteral()) {
156         panel.setTip(tipAttribute.getValue(faceletContext));
157       } else {
158         ValueExpression expression = tipAttribute.getValueExpression(faceletContext, String.class);
159         panel.setValueExpression(Attributes.TIP, expression);
160       }
161     }
162     if (labelAttribute != null) {
163       if (labelAttribute.isLiteral()) {
164         label.setValue(labelAttribute.getValue(faceletContext));
165       } else {
166         ValueExpression expression = labelAttribute.getValueExpression(faceletContext, String.class);
167         label.setValueExpression(Attributes.VALUE, expression);
168       }
169     }
170     if (markupAttribute != null) {
171       if (markupAttribute.isLiteral()) {
172         label.setMarkup(Markup.valueOf(markupAttribute.getValue()));
173       } else {
174         ValueExpression expression = markupAttribute.getValueExpression(faceletContext, Object.class);
175         label.setValueExpression(Attributes.MARKUP, expression);
176       }
177     }
178     panel.getChildren().add(label);
179   }
180 
181   private boolean checkForAlreadyCreated(UIComponent panel, String uid) {
182     if (panel.getChildCount() > 0) {
183       for (UIComponent child : panel.getChildren()) {
184         if (uid.equals(child.getId())) {
185           return true;
186         }
187       }
188     }
189     return false;
190   }
191 
192   public void onComponentPopulated(FaceletContext faceletContext, UIComponent component, UIComponent parent) {
193     super.onComponentPopulated(faceletContext, component, parent);
194     if (component.getChildren().size() > 1 && component.getChildren().get(1) instanceof EditableValueHolder) {
195       TobagoComponentHandler.addDefaultValidators(faceletContext.getFacesContext(),
196           (EditableValueHolder) component.getChildren().get(1));
197     }
198   }
199 
200   private void addGridLayout(FaceletContext faceletContext, UIComponent panel, UIViewRoot root) {
201     Application application = faceletContext.getFacesContext().getApplication();
202     UIGridLayout gridLayout = (UIGridLayout) application.createComponent(UIGridLayout.COMPONENT_TYPE);
203     gridLayout.setRendererType(RendererTypes.GRID_LAYOUT);
204     if (labelWidthAttribute != null) {
205       String columns = getColumns(labelWidthAttribute.getValue(faceletContext));
206       if (!LayoutUtils.checkTokens(columns)) {
207         LOG.warn("Illegal value for columns = \"" + columns + "\" replacing with default: \"" + DEFAULT_COLUMNS + "\"");
208         columns = DEFAULT_COLUMNS;
209       }
210       gridLayout.setColumns(columns);
211     } else {
212       gridLayout.setColumns(getColumns("auto"));
213     }
214     gridLayout.setRows(getRows());
215     gridLayout.setId(root.createUniqueId());
216     if (gridLayout instanceof OnComponentCreated) {
217       ((OnComponentCreated) gridLayout).onComponentCreated(faceletContext.getFacesContext(), panel);
218     }
219     panel.getFacets().put(Facets.LAYOUT, gridLayout);
220     if (gridLayout instanceof OnComponentPopulated) {
221       ((OnComponentPopulated) gridLayout).onComponentPopulated(faceletContext.getFacesContext(), panel);
222     }
223   }
224 
225   private void setSubComponentAttributes(FaceletContext ctx, Object instance) {
226     if (instance != null) {
227       Class type = instance.getClass();
228       if (subComponentMapper == null || !subComponentLastType.equals(type)) {
229         subComponentLastType = type;
230         subComponentMapper = createSubComponentMetaRuleset(type).finish();
231       }
232       subComponentMapper.applyMetadata(ctx, instance);
233     }
234   }
235 
236   protected MetaRuleset createSubComponentMetaRuleset(Class aClass) {
237     MetaRuleset metaRuleset = super.createMetaRuleset(aClass);
238     //metaRuleset.ignore(Attributes.LABEL);
239     metaRuleset.ignore(Attributes.TIP);
240     metaRuleset.ignore("labelWidth");
241     if (SupportsMarkup.class.isAssignableFrom(aClass)) {
242       metaRuleset.addRule(SupportsMarkupRule.INSTANCE);
243     }
244     if (InputSuggest.class.isAssignableFrom(aClass)) {
245       metaRuleset.addRule(SuggestMethodRule.INSTANCE);
246     }
247     return metaRuleset;
248   }
249 
250   protected MetaRuleset createMetaRuleset(Class aClass) {
251     MetaRuleset metaRuleset = super.createMetaRuleset(aClass);
252     TagAttribute [] attrs = tag.getAttributes().getAll();
253     for (int i = 0; i < attrs.length; i++) {
254       TagAttribute attr = attrs[i];
255       if (!attr.getLocalName().equals("rendered")) {
256         metaRuleset.ignore(attr.getLocalName());
257       }
258     }
259     return metaRuleset;
260   }
261 }