/
Converter.java
161 lines (141 loc) · 6.97 KB
/
Converter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
* Copyright 2019 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
*
* 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.omnifaces.taghandler;
import static org.omnifaces.taghandler.DeferredTagHandlerHelper.collectDeferredAttributes;
import static org.omnifaces.taghandler.DeferredTagHandlerHelper.createInstance;
import java.io.IOException;
import java.io.Serializable;
import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.convert.FacesConverter;
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.cdi.converter.ConverterManager;
import org.omnifaces.taghandler.DeferredTagHandlerHelper.DeferredAttributes;
import org.omnifaces.taghandler.DeferredTagHandlerHelper.DeferredTagHandler;
import org.omnifaces.taghandler.DeferredTagHandlerHelper.DeferredTagHandlerDelegate;
/**
* <p>
* The <code><o:converter></code> is a taghandler that extends the standard <code><f:converter></code> 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.
*
* <h3>Usage</h3>
* <p>
* When you specify for example the standard <code><f:convertDateTime></code> by
* <code>converterId="javax.faces.DateTime"</code>, then you'll be able to use all its attributes such as
* <code>pattern</code> and <code>locale</code> as per its documentation, but then with the possibility to supply
* deferred value expressions.
* <pre>
* <o:converter converterId="javax.faces.DateTime" pattern="#{item.pattern}" locale="#{item.locale}" />
* </pre>
* <p>
* The converter ID of all standard JSF converters can be found in
* <a href="http://docs.oracle.com/javaee/7/api/javax/faces/convert/package-summary.html">their javadocs</a>.
* First go to the javadoc of the class of interest, then go to <code>CONVERTER_ID</code> in its field summary
* and finally click the Constant Field Values link to see the value.
*
* <h3>JSF 2.3 compatibility</h3>
* <p>
* The <code><o:converter></code> is currently not compatible with converters which are managed via JSF 2.3's
* new <code>managed=true</code> attribute set on the {@link FacesConverter} annotation, at least not when using
* Mojarra. Internally, the converters are wrapped in another instance which doesn't have the needed setter methods
* specified. In order to get them to work with <code><o:converter></code>, the <code>managed=true</code>
* attribute needs to be removed, so that OmniFaces {@link ConverterManager} will automatically manage them.
*
* @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 <code>binding</code> and/or <code>converterId</code>
* attributes as per the standard JSF <code><f:converter></code> implementation and collect the render time
* attributes. Then create an anonymous <code>Converter</code> implementation which wraps the created
* <code>Converter</code> 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 <code>Converter</code> on.
* @throws IOException If something fails at I/O level.
*/
@Override
public void apply(FaceletContext context, UIComponent parent) throws IOException {
if (!ComponentHandler.isNew(parent) && UIComponent.getCompositeComponentParent(parent) == null) {
// If it's not new nor inside a composite component, we're finished.
return;
}
if (!(parent instanceof ValueHolder)) {
// It's likely a composite component. TagHandlerDelegate will pickup it and pass the target component back.
super.apply(context, parent);
return;
}
javax.faces.convert.Converter<Object> converter = createInstance(context, this, "converterId");
DeferredAttributes attributes = collectDeferredAttributes(context, this, converter);
((ValueHolder) parent).setConverter(new DeferredConverter() {
private static final long serialVersionUID = 1L;
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
attributes.invokeSetters(context.getELContext(), converter);
return converter.getAsObject(context, component, value);
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
attributes.invokeSetters(context.getELContext(), converter);
return converter.getAsString(context, component, value);
}
});
}
@Override
@SuppressWarnings("unchecked")
public <T> T create(Application application, String id) {
return (T) application.createConverter(id);
}
@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.
}
// Nested classes -------------------------------------------------------------------------------------------------
/**
* So that we can have a serializable converter.
*
* @author Bauke Scholtz
*/
protected abstract static class DeferredConverter implements javax.faces.convert.Converter<Object>, Serializable {
private static final long serialVersionUID = 1L;
}
}