| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.myfaces.trinidaddemo.composite; |
| |
| import java.io.IOException; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.Map; |
| |
| import javax.faces.application.FacesMessage; |
| import javax.faces.component.EditableValueHolder; |
| import javax.faces.component.UIComponent; |
| import javax.faces.context.FacesContext; |
| import javax.faces.context.ResponseWriter; |
| import javax.faces.convert.ConverterException; |
| import javax.faces.convert.NumberConverter; |
| import javax.faces.render.Renderer; |
| import javax.faces.validator.LongRangeValidator; |
| |
| import org.apache.myfaces.trinidad.component.core.input.CoreInputText; |
| |
| /** |
| * An experiment in building a renderer that uses a composite |
| * component strategy to render. Some basic principles: |
| * <ul> |
| * <li> The child components get re-created on each pass through |
| * the system; this means seeing if they exist in both Apply Request |
| * Values (<code>decode()</code>) and Render Response |
| * (<code>encodeBegin()</code>), and marking the components |
| * transient so they don't get saved. |
| * <li> The model is the tricky part: instead of using real |
| * <code>ValueBindings</code> on the children, I let them |
| * use local values, and then manully transfer over their local values |
| * into an overall "local value" during validate(). Unfortunately, |
| * using ValueBindings to automate the transfer wouldn't quite work, |
| * since the transfer wouldn't happen 'til Update Model, which is |
| * too late to preserve the semantics of an editable value component in JSF. |
| * </ul> |
| */ |
| public class DateFieldAsRenderer extends Renderer |
| { |
| @SuppressWarnings("unchecked") |
| @Override |
| public void decode(FacesContext context, UIComponent component) |
| { |
| _addChildren(context, component); |
| |
| Map<String, Object> attrs = component.getAttributes(); |
| if (Boolean.TRUE.equals(attrs.get("readOnly")) || |
| Boolean.TRUE.equals(attrs.get("disabled"))) |
| return; |
| |
| // Just clue in component that we have been "submitted" so |
| // that it doesn't short-circuit anything |
| EditableValueHolder evh = (EditableValueHolder) component; |
| evh.setSubmittedValue(Boolean.TRUE); |
| |
| // Because these components weren't around during processDecodes(), |
| // they didn't get decoded. So, run that now. |
| component.getFacet("month").processDecodes(context); |
| component.getFacet("year").processDecodes(context); |
| component.getFacet("day").processDecodes(context); |
| } |
| |
| @Override |
| public Object getConvertedValue( |
| FacesContext context, |
| UIComponent component, |
| Object submittedValue) |
| { |
| EditableValueHolder monthComp = (EditableValueHolder) component.getFacet("month"); |
| EditableValueHolder yearComp = (EditableValueHolder) component.getFacet("year"); |
| EditableValueHolder dayComp = (EditableValueHolder) component.getFacet("day"); |
| |
| if (!monthComp.isValid() || |
| !yearComp.isValid() || |
| !dayComp.isValid()) |
| { |
| // =-=AEW What to do???????? |
| //setValid(false); |
| return null; |
| } |
| |
| int year = ((Number) yearComp.getValue()).intValue(); |
| // We'll be 1970 - 2069. Good enough for a demo. |
| if (year < 70) |
| year += 100; |
| |
| int month = ((Number) monthComp.getValue()).intValue() - 1; |
| int day = ((Number) dayComp.getValue()).intValue(); |
| |
| Date oldValue = (Date) ((EditableValueHolder) component).getValue(); |
| //Date newValue = (Date) oldValue.clone(); |
| Calendar calendar = Calendar.getInstance(); |
| calendar.setLenient(true); |
| calendar.setTime(oldValue); |
| calendar.set(Calendar.YEAR, year); |
| calendar.set(Calendar.MONTH, month); |
| calendar.set(Calendar.DAY_OF_MONTH, day); |
| |
| // Invalid day given the month |
| if (day != calendar.get(Calendar.DAY_OF_MONTH)) |
| { |
| int numberOfDaysInMonth = day - calendar.get(Calendar.DAY_OF_MONTH); |
| FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, |
| "Invalid date.", |
| "This month only has " + numberOfDaysInMonth + " days!"); |
| throw new ConverterException(message); |
| } |
| |
| return calendar.getTime(); |
| } |
| |
| @Override |
| public void encodeBegin(FacesContext context, |
| UIComponent component) throws IOException |
| { |
| _addChildren(context, component); |
| } |
| |
| @Override |
| public void encodeChildren(FacesContext context, |
| UIComponent component) throws IOException |
| { |
| ResponseWriter out = context.getResponseWriter(); |
| |
| UIComponent month = component.getFacet("month"); |
| month.encodeBegin(context); |
| month.encodeChildren(context); |
| month.encodeEnd(context); |
| |
| out.writeText("\u00a0/\u00a0", null); |
| |
| UIComponent day = component.getFacet("day"); |
| day.encodeBegin(context); |
| day.encodeChildren(context); |
| day.encodeEnd(context); |
| |
| out.writeText("\u00a0/\u00a0", null); |
| |
| UIComponent year = component.getFacet("year"); |
| year.encodeBegin(context); |
| year.encodeChildren(context); |
| year.encodeEnd(context); |
| } |
| |
| @Override |
| public boolean getRendersChildren() |
| { |
| return true; |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void _addChildren(FacesContext context, UIComponent component) |
| { |
| // If the components are already there, bail. |
| if (component.getFacet("month") != null) |
| return; |
| |
| String id = component.getId(); |
| if (id == null) |
| { |
| id = context.getViewRoot().createUniqueId(); |
| component.setId(id); |
| } |
| |
| Map<String, UIComponent> facets = component.getFacets(); |
| facets.clear(); |
| |
| Date value = (Date) ((EditableValueHolder) component).getValue(); |
| Calendar calendar = null; |
| if(value != null) |
| { |
| calendar = Calendar.getInstance(); |
| calendar.setLenient(true); |
| calendar.setTime(value); |
| } |
| |
| CoreInputText month = _createTwoDigitInput(context); |
| month.setShortDesc("Month"); |
| month.setId(id + "_month"); |
| |
| LongRangeValidator monthRange = _createLongRangeValidator(context); |
| monthRange.setMinimum(1); |
| monthRange.setMaximum(12); |
| month.addValidator(monthRange); |
| if (value != null) |
| month.setValue(new Integer(calendar.get(Calendar.MONTH) + 1)); |
| facets.put("month", month); |
| |
| CoreInputText day = _createTwoDigitInput(context); |
| day.setShortDesc("Day"); |
| day.setId(id + "_day"); |
| LongRangeValidator dayRange = _createLongRangeValidator(context); |
| dayRange.setMinimum(1); |
| dayRange.setMaximum(31); |
| day.addValidator(dayRange); |
| if (value != null) |
| day.setValue(new Integer(calendar.get(Calendar.DAY_OF_MONTH))); |
| facets.put("day", day); |
| |
| CoreInputText year = _createTwoDigitInput(context); |
| year.setShortDesc("Year"); |
| year.setId(id + "_year"); |
| if (value != null) |
| { |
| int yearValue = calendar.get(Calendar.YEAR) - 1900; |
| if (yearValue >= 100) |
| yearValue -= 100; |
| year.setValue(new Integer(yearValue)); |
| } |
| |
| facets.put("year", year); |
| } |
| |
| private LongRangeValidator _createLongRangeValidator(FacesContext context) |
| { |
| return (LongRangeValidator) |
| context.getApplication().createValidator(LongRangeValidator.VALIDATOR_ID); |
| } |
| |
| private CoreInputText _createTwoDigitInput(FacesContext context) |
| { |
| CoreInputText input = new CoreInputText(); |
| input.setColumns(2); |
| input.setMaximumLength(2); |
| input.setTransient(true); |
| input.setRequired(true); |
| input.setSimple(true); |
| |
| NumberConverter converter = (NumberConverter) |
| context.getApplication().createConverter(NumberConverter.CONVERTER_ID); |
| converter.setIntegerOnly(true); |
| converter.setMaxIntegerDigits(2); |
| converter.setMinIntegerDigits(2); |
| input.setConverter(converter); |
| |
| return input; |
| } |
| } |