/
Param.java
144 lines (122 loc) · 4.93 KB
/
Param.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
/*
* Copyright 2021 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.component.output;
import static org.omnifaces.util.FacesLocal.createConverter;
import java.io.IOException;
import java.io.StringWriter;
import javax.faces.FacesException;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIParameter;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import org.omnifaces.component.ParamHolder;
/**
* <p>
* The <code><o:param></code> is a component that extends the standard {@link UIParameter} to implement {@link ValueHolder}
* and thus support a {@link Converter} to convert the supplied value to string, if necessary.
* <p>
* You can use it the same way as <code><f:param></code>, you only need to change <code>f:</code> into
* <code>o:</code> to get the extra support for a {@link Converter} by usual means via the <code>converter</code>
* attribute of the tag, or the nested <code><f:converter></code> tag, or just automatically if a converter is
* already registered for the target class via <code>@FacesConverter(forClass)</code>.
* <p>
* Also, if no value is specified, but children are present, then the encoded output of children will be returned as
* param value. This is useful when you want to supply JSF components or HTML as parameter of an unescaped
* <code><h:outputFormat></code>. For example,
* <pre>
* <h:outputFormat value="#{bundle.paragraph}" escape="false">
* <o:param><h:link outcome="contact" value="#{bundle.contact}" /></o:param>
* </h:outputFormat>
* </pre>
* <p>with this bundle
* <pre>
* paragraph = Please {0} for more information.
* contact = contact us
* </pre>
* <p>will result in the link being actually encoded as output format parameter value.
*
* @author Bauke Scholtz
* @param <T> The type of the value.
* @since 1.4
* @see ParamHolder
*/
@FacesComponent(Param.COMPONENT_TYPE)
public class Param<T> extends UIParameter implements ParamHolder<T> {
// Public constants -----------------------------------------------------------------------------------------------
public static final String COMPONENT_TYPE = "org.omnifaces.component.output.Param";
// Private constants ----------------------------------------------------------------------------------------------
private enum PropertyKeys {
// Cannot be uppercased. They have to exactly match the attribute names.
converter;
}
// Attribute getters/setters --------------------------------------------------------------------------------------
@Override
@SuppressWarnings("unchecked")
public Converter<T> getConverter() {
return (Converter<T>) getStateHelper().eval(PropertyKeys.converter);
}
@Override
@SuppressWarnings("rawtypes")
public void setConverter(Converter converter) {
getStateHelper().put(PropertyKeys.converter, converter);
}
/**
* @throws ClassCastException When actual value is not <code>T</code>.
*/
@Override
@SuppressWarnings("unchecked")
public T getLocalValue() {
return (T) super.getValue();
}
@Override
@SuppressWarnings("unchecked")
public String getValue() {
FacesContext context = getFacesContext();
Converter<T> converter = getConverter();
Object value = getLocalValue();
if (value == null && getChildCount() > 0) {
ResponseWriter originalResponseWriter = context.getResponseWriter();
StringWriter output = new StringWriter();
context.setResponseWriter(originalResponseWriter.cloneWithWriter(output));
try {
super.encodeChildren(context);
}
catch (IOException e) {
throw new FacesException(e);
}
finally {
context.setResponseWriter(originalResponseWriter);
}
value = output.toString();
}
if (converter == null && value != null) {
converter = createConverter(context, value.getClass());
}
if (converter != null) {
return converter.getAsString(context, this, (T) value);
}
else {
return value != null ? value.toString() : null;
}
}
@Override
public boolean getRendersChildren() {
return true;
}
@Override
public void encodeChildren(FacesContext context) throws IOException {
// This override which does nothing effectively blocks the children from being encoded during JSF render.
}
}