org.omnifaces.taghandler.Converter Maven / Gradle / Ivy
/*
* Copyright OmniFaces
*
* Licensed 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
*
* https://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.omnifaces.taghandler;
import static org.omnifaces.taghandler.DeferredTagHandlerHelper.collectDeferredAttributes;
import static org.omnifaces.taghandler.DeferredTagHandlerHelper.getValueExpression;
import java.io.IOException;
import java.io.Serializable;
import javax.el.ELContext;
import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.view.facelets.ComponentHandler;
import javax.faces.view.facelets.ConverterConfig;
import javax.faces.view.facelets.ConverterHandler;
import javax.faces.view.facelets.FaceletContext;
import javax.faces.view.facelets.TagAttribute;
import javax.faces.view.facelets.TagHandlerDelegate;
import org.omnifaces.taghandler.DeferredTagHandlerHelper.DeferredAttributes;
import org.omnifaces.taghandler.DeferredTagHandlerHelper.DeferredTagHandler;
import org.omnifaces.taghandler.DeferredTagHandlerHelper.DeferredTagHandlerDelegate;
import org.omnifaces.util.Callback;
/**
*
* The <o:converter>
is a taghandler that extends the standard <f:converter>
tag
* family with support for deferred value expressions in all attributes. In other words, the converter attributes are
* not evaluated anymore on a per view build time basis, but just on every access like as with UI components and bean
* properties. This has among others the advantage that they can be evaluated on a per-iteration basis inside an
* iterating component, and that they can be set on a custom converter without needing to explicitly register it in a
* tagfile.
*
*
Usage
*
* When you specify for example the standard <f:convertDateTime>
by
* converterId="javax.faces.DateTime"
, then you'll be able to use all its attributes such as
* pattern
and locale
as per its documentation, but then with the possibility to supply
* deferred value expressions.
*
* <o:converter converterId="javax.faces.DateTime" pattern="#{item.pattern}" locale="#{item.locale}" />
*
*
* The converter ID of all standard JSF converters can be found in
* their javadocs.
* First go to the javadoc of the class of interest, then go to CONVERTER_ID
in its field summary
* and finally click the Constant Field Values link to see the value.
*
* @author Bauke Scholtz
* @see DeferredTagHandlerHelper
*/
public class Converter extends ConverterHandler implements DeferredTagHandler {
// Constructors ---------------------------------------------------------------------------------------------------
/**
* The constructor.
* @param config The converter config.
*/
public Converter(ConverterConfig config) {
super(config);
}
// Actions --------------------------------------------------------------------------------------------------------
/**
* Create a {@link javax.faces.convert.Converter} based on the binding
and/or converterId
* attributes as per the standard JSF <f:converter>
implementation and collect the render time
* attributes. Then create an anonymous Converter
implementation which wraps the created
* Converter
and delegates the methods to it after setting the render time attributes. Finally set the
* anonymous implementation on the parent component.
* @param context The involved facelet context.
* @param parent The parent component to set the Converter
on.
* @throws IOException If something fails at I/O level.
*/
@Override
public void apply(FaceletContext context, UIComponent parent) throws IOException {
boolean insideCompositeComponent = UIComponent.getCompositeComponentParent(parent) != null;
if (!ComponentHandler.isNew(parent) && !insideCompositeComponent) {
// If it's not new nor inside a composite component, we're finished.
return;
}
if (!(parent instanceof ValueHolder) || (insideCompositeComponent && getAttribute("for") == null)) {
// It's inside a composite component and not reattached. TagHandlerDelegate will pickup it and pass the target component back if necessary.
super.apply(context, parent);
return;
}
ValueExpression binding = getValueExpression(context, this, "binding", Object.class);
ValueExpression id = getValueExpression(context, this, "converterId", String.class);
javax.faces.convert.Converter converter = createInstance(context.getFacesContext(), context, binding, id);
DeferredAttributes attributes = collectDeferredAttributes(context, this, converter);
((ValueHolder) parent).setConverter(new DeferredConverter(converter, binding, id, attributes));
}
@Override
public TagAttribute getTagAttribute(String name) {
return getAttribute(name);
}
@Override
protected TagHandlerDelegate getTagHandlerDelegate() {
return new DeferredTagHandlerDelegate(this, super.getTagHandlerDelegate());
}
@Override
public boolean isDisabled(FaceletContext context) {
return false; // This attribute isn't supported on converters anyway.
}
// Helpers --------------------------------------------------------------------------------------------------------
private static javax.faces.convert.Converter createInstance(final FacesContext facesContext, ELContext elContext, ValueExpression binding, ValueExpression id) {
return DeferredTagHandlerHelper.createInstance(elContext, binding, id, new Callback.ReturningWithArgument() {
@Override
public javax.faces.convert.Converter invoke(String converterId) {
return facesContext.getApplication().createConverter(converterId);
}
}, "converter");
}
// Nested classes -------------------------------------------------------------------------------------------------
/**
* So that we can have a serializable converter.
*
* @author Bauke Scholtz
*/
protected static class DeferredConverter implements javax.faces.convert.Converter, Serializable {
private static final long serialVersionUID = 1L;
private transient javax.faces.convert.Converter converter;
private final ValueExpression binding;
private final ValueExpression id;
private final DeferredAttributes attributes;
public DeferredConverter(javax.faces.convert.Converter converter, ValueExpression binding, ValueExpression id, DeferredAttributes attributes) {
this.converter = converter;
this.binding = binding;
this.id = id;
this.attributes = attributes;
}
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
javax.faces.convert.Converter converter = getConverter(context);
attributes.invokeSetters(context.getELContext(), converter);
return converter.getAsObject(context, component, value);
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
javax.faces.convert.Converter converter = getConverter(context);
attributes.invokeSetters(context.getELContext(), converter);
return converter.getAsString(context, component, value);
}
private javax.faces.convert.Converter getConverter(FacesContext context) {
if (converter == null) {
converter = Converter.createInstance(context, context.getELContext(), binding, id);
}
return converter;
}
}
}