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 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, 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     final Application application = faceletContext.getFacesContext().getApplication();
116 
117     addGridLayout(faceletContext, panel, application);
118     addLabel(faceletContext, (UIPanel) panel, application);
119 
120     String uid;
121     if (fieldIdAttribute != null) {
122       uid = fieldIdAttribute.getValue(faceletContext);
123     } else {
124       uid = "_tx_" + faceletContext.generateUniqueId("field");
125     }
126     if (checkForAlreadyCreated(panel, uid)) {
127       return;
128     }
129 
130     UIComponent input = application.createComponent(getSubComponentType());
131     input.setRendererType(getSubRendererType());
132     input.setId(uid);
133 
134     setSubComponentAttributes(faceletContext, input);
135     enrichInput(faceletContext, input);
136 
137     panel.getChildren().add(input);
138   }
139 
140   protected void enrichInput(FaceletContext faceletContext, UIComponent input) {
141   }
142 
143   private void addLabel(FaceletContext faceletContext, UIPanel panel, final Application application) {
144     final String uid = "_tx_" + faceletContext.generateUniqueId("label");
145     if (checkForAlreadyCreated(panel, uid)) {
146       return;
147     }
148     final UILabel label = (UILabel) application.createComponent(UILabel.COMPONENT_TYPE);
149     label.setRendererType(RendererTypes.LABEL);
150     label.setId(uid);
151     label.getAttributes().put(Attributes.FOR, "@auto");
152     if (tipAttribute != null) {
153       if (tipAttribute.isLiteral()) {
154         panel.setTip(tipAttribute.getValue(faceletContext));
155       } else {
156         final ValueExpression expression = tipAttribute.getValueExpression(faceletContext, String.class);
157         panel.setValueExpression(Attributes.TIP, expression);
158       }
159     }
160     if (labelAttribute != null) {
161       if (labelAttribute.isLiteral()) {
162         label.setValue(labelAttribute.getValue(faceletContext));
163       } else {
164         final ValueExpression expression = labelAttribute.getValueExpression(faceletContext, String.class);
165         label.setValueExpression(Attributes.VALUE, expression);
166       }
167     }
168     if (markupAttribute != null) {
169       if (markupAttribute.isLiteral()) {
170         label.setMarkup(Markup.valueOf(markupAttribute.getValue()));
171       } else {
172         final ValueExpression expression = markupAttribute.getValueExpression(faceletContext, Object.class);
173         label.setValueExpression(Attributes.MARKUP, expression);
174       }
175     }
176     panel.getChildren().add(label);
177   }
178 
179   private boolean checkForAlreadyCreated(UIComponent panel, String uid) {
180     if (panel.getChildCount() > 0) {
181       for (UIComponent child : panel.getChildren()) {
182         if (uid.equals(child.getId())) {
183           return true;
184         }
185       }
186     }
187     return false;
188   }
189 
190   public void onComponentPopulated(FaceletContext faceletContext, UIComponent component, UIComponent parent) {
191     super.onComponentPopulated(faceletContext, component, parent);
192 
193     if (component.getChildren().size() > 1) {
194       UIComponent input = component.getChildren().get(1);
195       if (input instanceof EditableValueHolder) {
196         TobagoComponentHandler.addDefaultValidators(faceletContext.getFacesContext(), (EditableValueHolder) input);
197       }
198       if (input instanceof OnComponentPopulated) {
199         ((OnComponentPopulated) input).onComponentPopulated(faceletContext.getFacesContext(), component);
200       }
201     }
202   }
203 
204   private void addGridLayout(FaceletContext faceletContext, UIComponent panel, final Application application) {
205     final UIGridLayout gridLayout = (UIGridLayout) application.createComponent(UIGridLayout.COMPONENT_TYPE);
206     gridLayout.setRendererType(RendererTypes.GRID_LAYOUT);
207     if (labelWidthAttribute != null) {
208       String columns = getColumns(labelWidthAttribute.getValue(faceletContext));
209       if (!LayoutUtils.checkTokens(columns)) {
210         LOG.warn("Illegal value for columns = \"" + columns + "\" replacing with default: \"" + DEFAULT_COLUMNS + "\"");
211         columns = DEFAULT_COLUMNS;
212       }
213       gridLayout.setColumns(columns);
214     } else {
215       gridLayout.setColumns(getColumns("auto"));
216     }
217     gridLayout.setRows(getRows());
218     gridLayout.setId("_tx_" + faceletContext.generateUniqueId("layout"));
219     if (gridLayout instanceof OnComponentCreated) {
220       ((OnComponentCreated) gridLayout).onComponentCreated(faceletContext.getFacesContext(), panel);
221     }
222     panel.getFacets().put(Facets.LAYOUT, gridLayout);
223     if (gridLayout instanceof OnComponentPopulated) {
224       ((OnComponentPopulated) gridLayout).onComponentPopulated(faceletContext.getFacesContext(), panel);
225     }
226   }
227 
228   private void setSubComponentAttributes(FaceletContext ctx, Object instance) {
229     if (instance != null) {
230       Class type = instance.getClass();
231       if (subComponentMapper == null || !subComponentLastType.equals(type)) {
232         subComponentLastType = type;
233         subComponentMapper = createSubComponentMetaRuleset(type).finish();
234       }
235       subComponentMapper.applyMetadata(ctx, instance);
236     }
237   }
238 
239   protected MetaRuleset createSubComponentMetaRuleset(Class aClass) {
240     MetaRuleset metaRuleset = super.createMetaRuleset(aClass);
241     //metaRuleset.ignore(Attributes.LABEL);
242     metaRuleset.ignore(Attributes.TIP);
243     metaRuleset.ignore("labelWidth");
244     if (SupportsMarkup.class.isAssignableFrom(aClass)) {
245       metaRuleset.addRule(SupportsMarkupRule.INSTANCE);
246     }
247     if (InputSuggest.class.isAssignableFrom(aClass)) {
248       metaRuleset.addRule(SuggestMethodRule.INSTANCE);
249     }
250     return metaRuleset;
251   }
252 
253   protected MetaRuleset createMetaRuleset(Class aClass) {
254     MetaRuleset metaRuleset = super.createMetaRuleset(aClass);
255     TagAttribute[] attrs = tag.getAttributes().getAll();
256     for (int i = 0; i < attrs.length; i++) {
257       TagAttribute attr = attrs[i];
258       if (!attr.getLocalName().equals("rendered")) {
259         metaRuleset.ignore(attr.getLocalName());
260       }
261     }
262     return metaRuleset;
263   }
264 }