Created a Tag Library Descriptor File:
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
<tlib-version>1.0</tlib-version>
<short-name>image</short-name>
<uri>/WEB-INF/tlds/image</uri>
<tag>
<name>BsilImage</name>
<tag-class>com.custom.image.ImageTagHandler</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>url</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>style</name>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
<attribute>
<name>type</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
ImageTagHandler
public class ImageTagHandler extends UIComponentELTag {
private String url;
private String style;
private String type;
private static final String IMAGE_COMP_TYPE = "IMAGE_COMPONENT";
private static final String IMAGE_RENDERER_TYPE = "IMAGE_RENDERER";
public void setUrl(String url) {
this.url = url;
}
public void setStyle(String style) {
this.style = style;
}
public void setType(String type) {
this.type = type;
}
public String getComponentType() {
return IMAGE_COMP_TYPE;
}
public String getRendererType() {
return IMAGE_RENDERER_TYPE;
}
#Override
protected void setProperties(UIComponent component) {
super.setProperties(component);
ImageComponent imgComponent =
((ImageComponent) component);
if (type != null) {
imgComponent.setType(ImageTranscoder.getImageTypeOf(type));
}
if (url != null) {
imgComponent.setUrl(ImageTranscoder.getImagePath(url));
}
if (style != null) {
imgComponent.setStyle(style);
}
}
}
ImageComponent
public class ImageComponent extends UIOutput {
private String url;
private String style;
private String type;
private static final String IMAGE_COMP_FAMILY = "IMAGE_FAMILY";
#Override
public String getFamily() {
return IMAGE_COMP_FAMILY;
}
/**
* Get the value of type
*
* #return the value of type
*/
public String getType() {
return type;
}
/**
* Set the value of type
*
* #param type new value of type
*/
public void setType(String type) {
this.type = type;
}
/**
* Get the value of style
*
* #return the value of style
*/
public String getStyle() {
return style;
}
/**
* Set the value of style
*
* #param style new value of style
*/
public void setStyle(String style) {
this.style = style;
}
/**
* Get the value of url
*
* #return the value of url
*/
public String getUrl() {
return url;
}
/**
* Set the value of url
*
* #param url new value of url
*/
public void setUrl(String url) {
this.url = url;
}
}
ImageRenderer
public class ImageRenderer extends Renderer {
#Override
public void encodeBegin(FacesContext context, UIComponent component)
throws IOException {
ImageComponent imgComponent = (ImageComponent)component;
ResponseWriter writer = context.getResponseWriter();
writer.startElement("div",component);
writer.startElement("img", imgComponent);
writer.writeAttribute("src",imgComponent.getUrl(), "url");
writer.writeAttribute("class", imgComponent.getStyle(), "style");
writer.endElement("div");
}
}
CODE TO ACCESS TAG LIB IS
<h:dataTable var="landing" binding="${LandingBean.tourPackages}" >
<h:column>
<tr:panelGroupLayout layout="vertical" styleClass="landingListing">
<bsil:BsilImage style="listingImage" url="${landing.photoThumbnail}" type="banner"/>
</tr:panelGroupLayout>
</h:column>
</h:dataTable>
The bean contain multiple photos which has to be shown in number of column when the page is displayed. The problem which I am facing is its cant show me multiple images gives an error saying
According to TLD or attribute
directive in tag file, attribute
binding does not accept any
expressions
If the bean contain single Image It is displayed the code for that is
<tr:panelHeader id="panelHeader" styleClass="banner" text="">
<bsil:BsilImage url="${LandingBean.bannerImage}"
style="bannerImage" type="banner"></bsil:BsilImage>
</tr:panelHeader>
I want to know how to display multiple images using custom component.
According to the error message you quoted, it seems that the issue is in the <h:dataTable ...> tag, it seems that its attribute called "binding" apparently doesn't accept expressions for some reason.
E.g.
<tag>
<name>dataTable</name>
<attribute>
<name>binding</name>
<!-- add this: -->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
If you own the code of the dataTable tag, add <rtexprvalue>true</rtexprvalue> next to the "binding" attribute of that tag (like you did in your image tag). If you don't, refer to it's documentation and find out why it doesn't allow expressions.
Related
In advance sorry for my bad english.
I have a problem when unmarshalling a string into a LocalDateTime in a multi thread environment. I generated the java classe (ActivityData) with org.apache.cxf version 2.7.7
When I debug the application from time to time the year of the string has been corrupted with some strange value at the beginning of the year (for instance 722008-01-01, A2008-01-01, 12008-01-01) when I make the call with a client like soap ui the date is well formatted. The localDateAdapter is called by multiple thread in a batch application. When I rerun the batch, the same call successes and others fails the parsing error is unpreddictable. The problem only occurs for the begin date the end date in the java object is always well formatted
I tried to synchronize the method but it doesn't change a thing.
Thanks in advance for your help
Here is the code of my LocalDateTimeAdapter
public class LocalDateTimeAdapter extends XmlAdapter<String, LocalDateTime> {
public synchronized LocalDateTime unmarshal(String v) {
if (StringUtils.isNotBlank(v)) {
try {
return LocalDateTime.parse(v, DateTimeFormatter.ISO_DATE_TIME);
} catch (Exception exception) {
throw new IllegalArgumentException("Parsing problem in the localDateTime for string : " + v);
}
}
return null;
}
public String marshal(LocalDateTime v) {
if (v != null) {
return v.toString();
}
return StringUtils.EMPTY;
}
}
The generated class :
the binding file :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxb:bindings
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:annox="http://annox.dev.java.net"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_1.xsd"
jaxb:extensionBindingPrefixes="xjc annox"
version="2.1">
<jaxb:globalBindings>
<jaxb:serializable uid="1"/>
<xjc:javaType
name="java.time.LocalDate"
xmlType="xs:date"
adapter="be.fgov.minfin.vies.adapter.LocalDateAdapter"/>
<xjc:javaType
name="java.time.LocalDateTime"
xmlType="xs:dateTime"
adapter="be.fgov.minfin.vies.adapter.LocalDateTimeAdapter"/>
</jaxb:globalBindings>
</jaxb:bindings>
the generated class :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "ActivityData", propOrder = {
"beginDate",
"endDate"
})
public class ActivityData
implements Serializable
{
private final static long serialVersionUID = 1L;
#XmlElement(type = String.class)
#XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
#XmlSchemaType(name = "dateTime")
protected LocalDateTime beginDate;
#XmlElement(type = String.class)
#XmlJavaTypeAdapter(LocalDateTimeAdapter.class)
#XmlSchemaType(name = "dateTime")
protected LocalDateTime endDate;
/**
* Gets the value of the beginDate property.
*
* #return
* possible object is
* {#link String }
*
*/
public LocalDateTime getBeginDate() {
return beginDate;
}
/**
* Sets the value of the beginDate property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setBeginDate(LocalDateTime value) {
this.beginDate = value;
}
/**
* Gets the value of the endDate property.
*
* #return
* possible object is
* {#link String }
*
*/
public LocalDateTime getEndDate() {
return endDate;
}
/**
* Sets the value of the endDate property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setEndDate(LocalDateTime value) {
this.endDate = value;
}
}
I couldn't manage to find any documentation on how to manage the tag on my custom Converter. I read many examples / tutos pointing out this tag, but when I apply it the same way I see in examples, the default value is never set to my absent-property, leaving it instead with the "deafault" null value.. or any value I set it to in the constructor (which is obviously a duplicate and thus I cannot considerate it a proper solution ).
If anyone has any tip about this....
To show out, the converter -whose property' default value is never 'set'- and its tag look like this.
faces-config :
<converter>
<converter-id>com.converter.currency</converter-id>
<converter-class>com.somedomain.converters.CurrencyConverter</converter-lass>
<property>
<property-name>nbDecimal</property-name>
<property-class>java.lang.Integer</property-class>
<default-value>2</default-value>
</property>
</converter>
taglib:
<tag>
<tag-name>convertCurrency</tag-name>
<converter>
<converter-id>com.converter.currency</converter-id>
<handler-class>com.somedomain.converters.CurrencyHandler</handler-class>
</converter>
</tag>
CurrencyHandler :
private final TagAttribute nbDecimal;
public CurrencyHandler(ConverterConfig config) {
super(config);
this.nbDecimal= this.getAttribute("nbDecimal");
}
// ...
CurrencyConverter :
private Integer nbDecimal; // + getter / setter
public CurrencyConverter() {
super();
}
public void init() {
Integer nbDecimal = getNbDecimal();
if (nbDecimal != null) {
setMinFractionDigits(nbDecimal);
setMaxFractionDigits(nbDecimal);
} else {
// this is where I could set them to default, which I don't want since it would not use the <default-value> in converter description
// unless that is the point where my mistake comes from...
}
}
public Object getAsObject(FacesContext context, UIComponent component, String value) {//...
}
public String getAsString(FacesContext context, UIComponent component, Object value) {//...
}
/**
*
*/
public Integer getNbDecimal() {
ELContext elContext = FacesContext.getCurrentInstance().getELContext();
if (getExpression() != null) {
Integer nbDecimal = (Integer) getExpression().getValue(elContext);
return nbDecimal;
} else {
return null;
}
}
}
I want to pass a list of strings as hidden input from a jsf page to the backing bean.
The backing bean is request scoped and needs to be it.
Trying to do it this way, but it doesn't seem to work. Any ideas how this can be done in a better way?
<ui:repeat value="#{bean.strings}" var="#{string}">
<h:inputHidden value="#{string}"/>
</ui:repeat>
Just use a converter for the list value:
<h:inputHidden value="#{bean.strings}" converter="myStringListConverter" />
Here is a converter that converts to/from a String using ### as separator:
#FacesConverter("myStringListConverter")
public class StringListConverter implements Converter {
// this is used as a regex, so choose other separator carefully
private static final String MY_SEPARATOR = "###";
#Override
public Object getAsObject(FacesContext context, UIComponent component,
String value) {
if (value == null) {
return new ArrayList<String>();
}
return new ArrayList<String>(Arrays.asList(value.split(MY_SEPARATOR)));
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object value) {
if (value == null) {
return "";
}
return join((List<String>) value, MY_SEPARATOR);
}
/**
* Joins a String list, src: http://stackoverflow.com/q/1751844/149872
*
* #param list
* #param conjunction
* #return
*/
public static String join(List<String> list, String conjunction) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (String item : list) {
if (first) {
first = false;
} else {
sb.append(conjunction);
}
sb.append(item);
}
return sb.toString();
}
}
If you are using JSF 2, this should work for you already as it is.
In case you are using JSF 1.2, you just have to drop the #FacesConverter annotation and register the converter in the faces-config.xml like so:
<converter>
<description>Simple String List Converer</description>
<converter-id>myStringListConverter</converter-id>
<converter-class>com.your.package.StringListConverter</converter-class>
</converter>
Using <resource-bundle> files I'm able to have i18n text in my JSF pages.
But is it possible to access these same properties in my managed bean so I can set faces messages with i18n values?
Assuming that you've configured it as follows:
<resource-bundle>
<base-name>com.example.i18n.text</base-name>
<var>text</var>
</resource-bundle>
If your bean is request scoped, you can just inject the <resource-bundle> as #ManagedProperty by its <var>:
#ManagedProperty("#{text}")
private ResourceBundle text;
public void someAction() {
String someKey = text.getString("some.key");
// ...
}
Or if you just need some specific key:
#ManagedProperty("#{text['some.key']}")
private String someKey;
public void someAction() {
// ...
}
If your bean is however in a broader scope, then evaluate #{text} programmatically in method local scope:
public void someAction() {
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle text = context.getApplication().evaluateExpressionGet(context, "#{text}", ResourceBundle.class);
String someKey = text.getString("some.key");
// ...
}
Or if you only need some specific key:
public void someAction() {
FacesContext context = FacesContext.getCurrentInstance();
String someKey = context.getApplication().evaluateExpressionGet(context, "#{text['some.key']}", String.class);
// ...
}
You can even just get it by the standard ResourceBundle API the same way as JSF itself is already doing under the covers, you'd only need to repeat the base name in code:
public void someAction() {
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle text = ResourceBundle.getBundle("com.example.i18n.text", context.getViewRoot().getLocale());
String someKey = text.getString("some.key");
// ...
}
Or if you're managing beans by CDI instead of JSF, then you can create a #Producer for that:
public class BundleProducer {
#Produces
public PropertyResourceBundle getBundle() {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().evaluateExpressionGet(context, "#{text}", PropertyResourceBundle.class);
}
}
And inject it as below:
#Inject
private PropertyResourceBundle text;
Alternatively, if you're using the Messages class of the JSF utility library OmniFaces, then you can just set its resolver once to let all Message methods utilize the bundle.
Messages.setResolver(new Messages.Resolver() {
public String getMessage(String message, Object... params) {
ResourceBundle bundle = ResourceBundle.getBundle("com.example.i18n.text", Faces.getLocale());
if (bundle.containsKey(message)) {
message = bundle.getString(message);
}
return MessageFormat.format(message, params);
}
});
See also the example in the javadoc and the showcase page.
Another possibility:
faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<faces-config ...>
<application>
<locale-config>
<default-locale>de</default-locale>
</locale-config>
<resource-bundle>
<base-name>de.fhb.resources.text.backend</base-name>
<var>backendText</var>
</resource-bundle>
</application>
</faces-config>
YourBean.java
FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
ResourceBundle backendText = app.getResourceBundle(context, "backendText");
backendText.getString("your.property.key");
Here is a solution I'm using, not as simple but at least working :
First class :
package com.spectotechnologies.website.util;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator;
/**
*
* #author Alexandre Lavoie
*/
public class MessageInterpolator extends ResourceBundleMessageInterpolator
{
public static final String LANGUAGE_TAG_PATTERN = "\\{[^}]*\\}";
#Override
public String interpolate(String p_sMessage, Context p_oContext)
{
return super.interpolate(replaceMessages(p_sMessage),p_oContext);
}
#Override
public String interpolate(String p_sMessage, Context p_oContext, Locale p_oLocale)
{
StringManager.setLocale(p_oLocale);
return super.interpolate(replaceMessages(p_sMessage),p_oContext,p_oLocale);
}
private String replaceMessages(String p_sMessage)
{
Matcher oMatcher;
String sKey;
String sReplacement;
StringBuffer sbTemp = new StringBuffer();
oMatcher = Pattern.compile(LANGUAGE_TAG_PATTERN).matcher(p_sMessage);
while(oMatcher.find())
{
sKey = oMatcher.group().substring(1,oMatcher.group().length() - 1);
sReplacement = StringManager.getString(sKey);
if(!sReplacement.startsWith("???"))
{
oMatcher.appendReplacement(sbTemp,sReplacement);
}
}
oMatcher.appendTail(sbTemp);
return sbTemp.toString();
}
}
Second class :
package com.spectotechnologies.website.util;
import com.spectotechnologies.util.BundleManager;
import com.spectotechnologies.util.FacesSessionManager;
import java.util.Locale;
/**
* set-up and interface a BundleManager
* save the bundleManager into the session
* #author Charles Montigny
*/
public final class StringManager
{
/** the session name of this class bundle manager */
public static final String BUNDLE_MANAGER_SESSION_NAME = "StringManager_BundleManager";
/** List of ResourceBundle names to load.
* Will be load in order.
* The last ones values may overrite the first ones values. */
private final static String[] BUNDLE_LIST = {
"com.spectotechnologies.hibernate.validation.resources.ValidationMessages",
"com.spectotechnologies.website.general.resources.ValidationMessages",
"com.spectotechnologies.website.general.resources.General"};
/** bundle manager */
private static BundleManager m_oBundleManager = null;
private static BundleManager getBundleManager()
{
if(m_oBundleManager == null)
{
// get the bundle into the session
m_oBundleManager = (BundleManager)FacesSessionManager.getObject(BUNDLE_MANAGER_SESSION_NAME);
if(m_oBundleManager == null)
{
// session was empty, load a new one
m_oBundleManager = new BundleManager();
for(int iIndex = 0; iIndex < BUNDLE_LIST.length; iIndex++)
{
m_oBundleManager.addBundle(BUNDLE_LIST[iIndex]);
}
// add the bundle to the session
FacesSessionManager.setObject(BUNDLE_MANAGER_SESSION_NAME, m_oBundleManager);
}
}
return m_oBundleManager;
}
/**
* get a string value in the bundle manager by a string key
* #param p_sKey the string key
* #return the value of the string key
*/
public static String getString(String p_sKey)
{
return getBundleManager().getStringValue(p_sKey);
}
/**
* set the locale
* #param p_oLocale the locale to set
*/
public static void setLocale(Locale p_oLocale)
{
getBundleManager().setLocale(p_oLocale);
// update the session
FacesSessionManager.setObject(BUNDLE_MANAGER_SESSION_NAME,
getBundleManager());
}
}
After you need to override BUNDLE_LIST with your properties files. Once done, use it like that :
sMessage = StringManager.getString("website.validation.modifySystem.modified");
If you have questions, do not hesitate!
EDIT :
You also need to declare the MessageInterpolator
META-INF/validation.xml
<validation-config
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration">
<message-interpolator>com.spectotechnologies.website.util.MessageInterpolator</message-interpolator>
</validation-config>
I'm trying to implement a custom truncate converter, which truncates a string at a given index and adds a continuation symbol. The converter works fine, only when i hard code the parameters, as they are not being passed to the backend. What am I doing wrong?
The parameters are properties of the converter class:
#FacesConverter(value = TruncateConverter.CONVERTER_ID)
public class TruncateConverter implements Converter, StateHolder
{
public static final String CONVERTER_ID = "bla.blablabla.Truncate";
private int truncateIndex;
private String contSymbol;
Here is how i'm using the converter (or trying to):
<h:outputText id="news-text-left" value="#{newsListBean.newsList_teaser.text}">
<f:converter converterId="bla.blablabla.Truncate" truncateIndex="150" contSymbol="..." />
</h:outputText>
I googled around for quite a bit and wasn't able to find a single example of a JSF2 converter with parameters... Thank you guys for your help, really appreciate it!
You may take a look at JSF2.0 sources. For example DateTimeConverter... JSF sources available here in svn reposotory: https://svn.java.net/svn/mojarra~svn/trunk
IMO creating such converter is not easy. It also required to create converter tag to register converter.
Other way to pass some data to converter is attibutes. So You can write
<h:outputText ...>
<f:converter converterId="bla.blablabla.Truncate" />
<f:attribute name="truncateIndex" value="150"/>
</h:outputText>
Than call to component.getAttributes().get("truncateIndex");
in converter code.
Based on #Maks solution: It's possible to combine the converter and the attribute in one tag:
<facelet-taglib version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">
<namespace>http://mycompany.com/some-identifier</namespace>
<tag>
<tag-name>truncate</tag-name>
<converter>
<converter-id>bla.blablabla.Truncate</converter-id>
</converter>
<attribute>
<name>truncateIndex</name>
</attribute>
</tag>
</facelet-taglib>
Then you can use the converter like this:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:my="http://mycompany.com/some-identifier">
<my:truncate truncateIndex="150" />
</ui:composition>
You also don't need to grab the parameter from the component-attributes. A bean-property with the same name will be populated automatically:
#FacesConverter("bla.blablabla.Truncate")
public class Truncate implements Converter {
private String truncateIndex;
// getters + setters
...
}
http://jerryorr.blogspot.nl/2011/10/creating-jsf-12-custom-converter-with.html is a good guide to set up your first custom converter with parameters
This is how I did it (in a separated jar file, and using maven's default directories):
1) Create the converter's class (inside src/main/java)
2) Create a .taglib.xml class (inside src/main/resources/META-INF)
3) Create a faces-config.xml (inside src/main/resources/META-INF)
Example:
Step 1)
package com.ocabit.jsf.converter;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
import org.springframework.stereotype.Service;
import com.ocabit.utils.currency.CurrencyUtils;
/**
* Converter para valores BigDecimal.
*
* #author Carlos Eduardo Pauluk
*
*/
#FacesConverter("bigDecimalConverter")
#Service("bigDecimalConverter")
public class BigDecimalConverter implements Converter, Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String CONVERTER_ID = "com.ocabit.jsf.converter.BigDecimalConverter";
/**
* Em caso de null, força a saída para 0.0;
*/
private boolean nullToZero = false;
/**
* Em caso de zero, força a saída para null;.
*/
private boolean zeroToNull = false;
/**
* Só retorna números positivos.
*/
private boolean onlyAbs = false;
/**
* Só retorna números negativos.
*/
private boolean onlyNeg = false;
/**
* Quantidade de casas decimais.
*/
private int decimals = 2;
#Override
public Object getAsObject(final FacesContext context, final UIComponent component, final String value) {
try {
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(getDecimals(), RoundingMode.HALF_DOWN);
if (bd.equals(BigDecimal.ZERO) && isZeroToNull()) {
return null;
}
if (isOnlyAbs()) {
bd = bd.abs();
}
if (isOnlyNeg()) {
bd = bd.abs();
bd = bd.negate();
}
return bd;
} catch (final Exception e) {
BigDecimal bd = null;
if (isNullToZero()) {
bd = CurrencyUtils.getBigDecimalCurrency("0.0");
bd = bd.setScale(getDecimals(), RoundingMode.HALF_DOWN);
}
return bd;
}
}
#Override
public String getAsString(final FacesContext context, final UIComponent component, final Object value) {
if (!(value instanceof BigDecimal)) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erro ao converter o valor decimal.", ""));
}
final NumberFormat nf = NumberFormat.getInstance();
// sempre terá 2 casas decimais
nf.setMinimumFractionDigits(2);
nf.setMaximumFractionDigits(2);
return nf.format(((BigDecimal) value).doubleValue());
}
public boolean isNullToZero() {
return nullToZero;
}
public void setNullToZero(boolean nullToZero) {
this.nullToZero = nullToZero;
}
public boolean isZeroToNull() {
return zeroToNull;
}
public void setZeroToNull(boolean zeroToNull) {
this.zeroToNull = zeroToNull;
}
public boolean isOnlyAbs() {
return onlyAbs;
}
public void setOnlyAbs(boolean onlyAbs) {
this.onlyAbs = onlyAbs;
}
public boolean isOnlyNeg() {
return onlyNeg;
}
public void setOnlyNeg(boolean onlyNeg) {
this.onlyNeg = onlyNeg;
}
public int getDecimals() {
return decimals;
}
public void setDecimals(int decimals) {
this.decimals = decimals;
}
}
Step 2)
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"facelet-taglib_1_0.dtd">
<facelet-taglib version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facelettaglibrary_2_2.xsd">
<namespace>http://ocabit.com.br/facelets</namespace>
<tag>
<tag-name>convertBigDecimal</tag-name>
<converter>
<converter-id>com.ocabit.jsf.converter.BigDecimalConverter</converter-id>
</converter>
</tag>
</facelet-taglib>
Step 3)
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
<converter>
<description></description>
<converter-id>com.ocabit.jsf.converter.BigDecimalConverter</converter-id>
<converter-class>com.ocabit.jsf.converter.BigDecimalConverter</converter-class>
<property>
<property-name>nullToZero</property-name>
<property-class>boolean</property-class>
<description>Ao invés de retornar 'null', retorna '0.0'</description>
</property>
<property>
<property-name>zeroToNull</property-name>
<property-class>boolean</property-class>
<description>Ao invés de retornar '0.0', retorna 'null'</description>
</property>
<property>
<property-name>onlyAbs</property-name>
<property-class>boolean</property-class>
<description>Somente retorna números positivos</description>
</property>
<property>
<property-name>onlyNeg</property-name>
<property-class>boolean</property-class>
<description>Somente retorna números negativos</description>
</property>
<property>
<property-name>decimals</property-name>
<property-class>int</property-class>
<description>Quantidade de casas decimais</description>
</property>
</converter>
</faces-config>
Voilà.