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