Trim String in JSF h:outputText value - jsf

Is there any way to string JSF h:outPutTextValue ? my string is A-B-A03 ,i just want to display last 3 characters .does openfaces have any avialable function to do this ?
Thanks

You could use a Converter for this job. JSF has several builtin converters, but no one suits this very specific functional requirement, so you'd need to create a custom one.
It's relatively easy, just implement the Converter interface according its contract:
public class MyConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
// Write code here which converts the model value to display value.
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
// Write code here which converts the submitted value to model value.
// This method won't be used in h:outputText, but in UIInput components only.
}
}
Provided that you're using JSF 2.0 (your question history confirms this), you can use the #FacesConverter annotation to register the converter. You can use the (default) value attribute to assign it a converter ID:
#FacesConverter("somethingConverter")
(where "something" should represent the specific name of the model value you're trying to convert, e.g. "zipcode" or whatever it is)
so that you can reference it as follows:
<h:outputText value="#{bean.something}" converter="somethingConverter" />
For your particular functional requirement the converter implementation can look like this (assuming that you actually want to split on - and return only the last part, which makes so much more sense than "display last 3 characters"):
#FacesConverter("somethingConverter")
public class SomethingConverter implements Converter {
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) throws ConverterException {
if (!(modelValue instanceof String)) {
return modelValue; // Or throw ConverterException, your choice.
}
String[] parts = ((String) modelValue).split("\\-");
return parts[parts.length - 1];
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) throws ConverterException {
throw new UnsupportedOperationException("Not implemented");
}
}

You can try and use the fn:substring functions from JSTL:
${fn:substring('A-B-A03', 4, 7)}

If your string comes from a bean you can add an extra getter to return the trimmed version:
private String myString = "A-B-A03";
public String getMyStringTrimmed()
{
// You could also use java.lang.String.substring with some ifs here
return org.apache.commons.lang.StringUtils.substring(myString, -3);
}
Now you can use the getter in your JSF page:
<h:outputText value="#{myBean.myStringTrimmed}"/>

Related

JSF 2.3 Custom converter with generics

We now started to use JSF 2.3 on our existing JSF 2.2 project. On our custom converters we got warning Converter is a raw type. References to generic type Converter<T> should be parameterized.
Problem that we experiencing is when we tried to fix that warning using generics:
#FacesConverter(value = "myConverter", managed = true)
public class MyConverter implements Converter<MyCustomObject>{
#Override
public MyCustomObject getAsObject(FacesContext context, UIComponent component, String submittedValue){}
#Override
public String getAsString(FacesContext context, UIComponent component, MyCustomObject modelValue) {}
}
and when converter is used for example in
<!DOCTYPE html>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:selectOneMenu id="#{componentId}" value="#{componentValue}">
<f:converter converterId="myConverter" />
<f:selectItem itemLabel="label"
itemValue="" />
<f:selectItems value="listOfValues"
var="singleValue"
itemValue="singleValue.value"
itemLabel="singleValue.label" />
</h:selectOneMenu>
then ClassCastException with message java.lang.String cannot be cast to MyCustomObjectis thrown. There is also one line in stacktrace that maybe can help com.sun.faces.cdi.CdiConverter.getAsString(CdiConverter.java:109).
But when converter generics definition changed from MyCustomObject to Object :
#FacesConverter(value = "myConverter", managed = true)
public class MyConverter implements Converter<Object>{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue){}
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {}
}
then everything works as expected, but that obviously beats the purpose of Converter<T> interface.
I had same issue here and came up with following solution that actually not just compiles but also runs well:
some_page.xhtml (relevant excerpt):
<h:selectOneMenu styleClass="select" id="companyUserOwner" value="#{adminCompanyDataController.companyUserOwner}">
<f:converter converterId="UserConverter" />
<f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
<f:selectItems value="#{userController.allUsers()}" var="companyUserOwner" itemValue="#{companyUserOwner}" itemLabel="#{companyUserOwner.userContact.contactFirstName} #{companyUserOwner.userContact.contactFamilyName} (#{companyUserOwner.userName})" />
</h:selectOneMenu>
Please note that the above JSF code is full of custom stuff and may not work on your end. And that my converter compiles without rawtype warning (JSF 2.3+, not 2.2!):
SomeUserConverter.java:
#FacesConverter (value = "UserConverter")
public class SomeUserConverter implements Converter<User> {
/**
* User EJB
*/
private static UserSessionBeanRemote USER_BEAN;
/**
* Default constructor
*/
public SomeUserConverter () {
}
#Override
public User getAsObject (final FacesContext context, final UIComponent component, final String submittedValue) {
// Is the value null or empty?
if ((null == submittedValue) || (submittedValue.trim().isEmpty())) {
// Return null
return null;
}
// Init instance
User user = null;
try {
// Try to parse the value as long
final Long userId = Long.valueOf(submittedValue);
// Try to get user instance from it
user = USER_BEAN.findUserById(userId);
} catch (final NumberFormatException ex) {
// Throw again
throw new ConverterException(ex);
} catch (final UserNotFoundException ex) {
// User was not found, return null
}
// Return it
return user;
}
#Override
public String getAsString (final FacesContext context, final UIComponent component, final User value) {
// Is the object null?
if ((null == value) || (String.valueOf(value).isEmpty())) {
// Is null
return ""; //NOI18N
}
// Return id number
return String.valueOf(value.getUserId());
}
}
This converter class has the JNDI lookup removed (I will rewrite that part later anyway) but it should be enough for demonstrating the important parts:
Converter<User> (by User is a custom POJI) preventing raw-type warning
value="UserConverter" that is the actual name you use in your JSF page
And most important, which actually fixed the ClassCastException:
<f:selectItem itemValue="#{null}" itemLabel="#{msg.NONE_SELECTED}" />
By omitting #{null} an empty string instead of null is being handled over to your getAsString method!
This last one actually fixed the said exception and my application is working again with much lesser warnings and much better type-hinting.
Need to remove forClass as value is there: FacesConverter is using both value andforClass, only value will be applied.
The raw-type warning will come (since Java SE 5) because you use an interface which has a generic (mostly stated as <T> after the interface name) which you need to satisfy so the compiler stop throwing that warning at you.
I had two issues with the FacesConverter.
First one, when I didn't use forClass = MyCustomObject.class I got the exact same error message as you had. Putting the forClass attribute in the #FacesConverter annotation solved the issue. It should solve it also for you...
Secondly, I suppose this is happening in the getAsString method? I had another issue with String casting and I had to do: return myCustomObject.getId().toString() where id is of type Long. After that I adapted my getAsObject to fetch the MyCustomObject based on the id. Maybe it is not directly related to your problem, but I thought it was important to mention as it is something I often bump into when writing converters.

java.math.BigDecimal in p:selectOneMenu

Converter :
#FacesConverter("bigDecimalConverter")
public class BigDecimalConverter implements Converter {
private static final int SCALE = 2;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (value == null || value.isEmpty()) {
return null;
}
try {
return new BigDecimal(value);
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, null, "Message"), e);
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value == null) {
return "";
}
BigDecimal newValue;
if (value instanceof Long) {
newValue = BigDecimal.valueOf((Long) value);
} else if (value instanceof Double) {
newValue = BigDecimal.valueOf((Double) value);
} else if (!(value instanceof BigDecimal)) {
throw new ConverterException("Message");
} else {
newValue = (BigDecimal) value;
}
DecimalFormat formatter = (DecimalFormat) NumberFormat.getNumberInstance();
formatter.setGroupingUsed(false);
formatter.setMinimumFractionDigits(SCALE);
formatter.setMaximumFractionDigits(SCALE);
return formatter.format(newValue);
}
}
List :
<p:selectOneMenu id="list" value="#{bean.value}">
<f:selectItems var="row" value="#{bean.list}" itemLabel="#{row}" itemValue="#{row}"/>
<f:converter converterId="bigDecimalConverter"/>
</p:selectOneMenu>
<p:message id="msg" for="list"/>
<p:commandButton value="Submit" update="list msg" actionListener="#{bean.action}"/>
The managed bean backed by the above <p:selectOneMenu> :
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
private List<BigDecimal> list; // Getter only.
private BigDecimal value; // Getter & setter.
private static final long serialVersionUID = 1L;
public Bean() {}
#PostConstruct
private void init() {
list = new ArrayList<BigDecimal>(){{
add(BigDecimal.valueOf(10));
add(BigDecimal.valueOf(20.11));
add(BigDecimal.valueOf(30));
add(BigDecimal.valueOf(40));
add(BigDecimal.valueOf(50));
}};
}
public void action() {
System.out.println("action() called : " + value);
}
}
A validation message, "Validation Error: Value is not valid" appears upon submission of the form. The getAsObject() method throws no exception upon form submission.
If a value with a scale like 20.11 is selected in the list, then the validation passes. It appears that the equals() method in the java.math.BigDecimal class goes fishy which for example, considers two BigDecimal objects equal, only if both of them are equal in value and scale thus 10.0 != 10.00 which requires compareTo() for them to be equal.
Any suggestion?
You lose information when converting to String. The default JSF BigDecimalConverter does this right, it uses BigDecimal#toString in its getAsString. The BigDecimal#toString's javadoc says:
There is a one-to-one mapping between the distinguishable BigDecimal values and the result of this conversion. That is, every distinguishable BigDecimal value (unscaled value and scale) has a unique string representation as a result of using toString. If that string representation is converted back to a BigDecimal using the BigDecimal(String) constructor, then the original value will be recovered.
This is exactly what you need. Don't treat converters like they must produce user readable-writable results when converting to String. They mustn't and often don't. They produce a String representation of an object, or an object reference. That's it. The selectItems' itemLabel defines a user readable representation in this case. I'm assuming that you don't want user writable values here, that you really have a fixed list of values for user to choose from.
If you really mean that this data must always have a scale of 2, and you need a user writable value, then that would be better checked in a validator, and user input could be helped out with p:inputMask.
Finally, let's set aside the fact, that your converter is not the best. It says "data must have a scale of 2". Then your should provide conforming data in your selectItems. More generally, server defined values must conform to relevant converters and validators. E.g. you could have problems in the same vein, when using the DateTimeConverter with the pattern "dd.MM.yyyy", but setting the default value to be new Date() without getting rid of the time part.
See also (more general notions about converters): https://stackoverflow.com/a/30529976/1341535

Format JSF phone number

I am storing a phone number as a string in our database. It would be stored like "1234567890".
I want to display that to the user and format it like (12) 3456-7890
How can I do this with JSF 2.0?
I tried the following, but it doesn't work:
<h:outputText value="1234567890">
<f:convertNumber pattern="(##) ####-####"/>
</h:outputText>
The <f:convertNumber> uses DecimalFormat under the covers and this isn't designed with phone numbers in mind.
You'd need to create a custom Converter and do the desired job in getAsString() implementation using the usual String methods such as substring() and friends.
#FacesConverter("phoneConverter")
public class PhoneConverter implements Converter{
#Override
public String getAsString(FacesContext context, UIComponent component, Object modelValue) {
String phoneNumber = (String) modelValue;
StringBuilder formattedPhoneNumber = new StringBuilder();
// ...
return formattedPhoneNumber.toString();
}
#Override
public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) {
// Conversion is not necessary for now. However, if you ever intend to use
// it on input components, you probably want to implement it here.
throw new UnsupportedOperationException("Not implemented yet");
}
}
Use it as follows:
<h:outputText value="1234567890" converter="phoneConverter" />
As BalusC answered, you need a converter to do so. I would suggest the following though: instead of writing your own formatting logic, use a library like libphonenumber. This will do all the hard work for you. Best practice here would be storing the phone number in your model in E.164 format.

How to register a custom renderer in JSF?

We have numerical values in our database, representing a two-value-state. Of course this would perfectly match a boolean, but oracle has no such datatype. The NUMBER(1,0) type from the database is matched to a java.lang.Short type in Java (sometimes they used a NUMBER(*,0) to represent booleans, which are matched to java.math.BigDecimal).
Since it is somehow obvious, I want to offer ice:selectBooleanCheckbox in the view as a value representation and UIComponent to the user. (I use IceFaces as JSF implementation)
Since some people who specified JSF think it is obvious to always match the value of a ice:selectBooleanCheckbox or in JSF h:selectBooleanCheckbox to a boolean in the model, so the renderer of the component never calls any converter, even if you specify one:
Issue disscused at java.net
Therefore I tried the following:
I created a converter to specify it in the UIComponent:
public class BooleanBigDecimalConverter implements Converter {
public Object getAsObject(FacesContext context, UIComponent component, String str) {
if (StringUtils.isEmptyString(str)) {
return new BigDecimal(0);
}
if (str.equals("true")) {
return new BigDecimal(1);
} else {
return new BigDecimal(0);
}
}
public String getAsString(FacesContext context, UIComponent component, Object obj) {
if (obj != null) {
String str = obj.toString();
if (str.equalsIgnoreCase("1")
|| str.equalsIgnoreCase("yes")
|| str.equalsIgnoreCase("true")
|| str.equalsIgnoreCase("on")) {
return "true";
} else {
return "false";
}
}
return "false";
}
}
The converter works fine for the render phase (the getAsString-method is called correctly), but the getAsObject-method (Ignore that it's not correct at the moment, because it's not called anyway, so it will be fixed if it's called!) is never called, because in the renderer of the UIComponent a converter is not foreseen, like you can see here (snip from com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer):
public Object getConvertedValue(FacesContext facesContext, UIComponent uiComponent, Object submittedValue) throws ConverterException
{
if(!(submittedValue instanceof String))
throw new ConverterException("Expecting submittedValue to be String");
else
return Boolean.valueOf((String)submittedValue);
}
So this results in an IllegalArgumentException, since in the UpdateModelValues phase it is tried to apply a Boolean to a numerical value (please ignore the BigDecimal/Short confusion... it is just a numerical type in any case!).
So I tried to overwrite the renderer with a new one like this:
import com.icesoft.faces.component.ext.renderkit.CheckboxRenderer;
public class CustomHtmlSelectBooleanCheckbox extends CheckboxRenderer {
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
Converter converter = ((ValueHolder) component).getConverter();
return converter.getAsObject(context, component, (String) submittedValue);
}
}
and registered it like this in faces-config.xml:
<render-kit>
<renderer>
<component-family>com.icesoft.faces.HtmlSelectBooleanCheckbox</component-family>
<renderer-type>com.icesoft.faces.Checkbox</renderer-type>
<renderer-class>com.myapp.web.util.CustomHtmlSelectBooleanCheckbox</renderer-class>
</renderer>
</render-kit>
I guess this should be correct, but the overridden method "getConvertedValue" is never called, nor is the getAsObject()-method, so I guess I made a mistake in registering the custom renderer, but I can't find any more documentation or hints how to do this properly and especially how to find the correct component-family (I looked up the one I use in icefaces.taglib.xml) and the correct renderer-type.
I don't want to edit the complete model because of this. Any hints, how this can be resolved?
We could fix the problem and correctly register our custom renderer.
The problem was to find the correct properties for the intended renderer. Our tries were wrong, since I found out how to get the appropriate information. It's a bit of work and searching, but it finally did the trick.
Just start your container in debug mode and add a breakpoint on class level into the derived class the custom renderer is based on (in my case com.icesoft.faces.renderkit.dom_html_basic.CheckboxRenderer).
During container start-up this breakpoint will be reached and in the stacktrace you'll find a call of the method FacesConfigurator.configureRenderKits().
This object contains an ArrayList of registered renderers. I searched the list for the renderer I'd have liked to overwrite and could find the informations I need to register my custom renderer. In my case this is the correct entry in faces-config.xml:
<render-kit>
<description>The ICEsoft Renderers.</description>
<render-kit-id>ICEfacesRenderKit</render-kit-id>
<render-kit-class>com.icesoft.faces.renderkit.D2DRenderKit</render-kit-class>
<renderer>
<component-family>javax.faces.SelectBoolean</component-family>
<renderer-type>com.icesoft.faces.Checkbox</renderer-type>
<renderer-class>com.myapp.web.util.CustomHtmlSelectBooleanCheckbox</renderer-class>
</renderer>
</render-kit>
Now the getAsObject()-method in the converter is called by the custom renderer. Be sure to override the method correctly, in case you don't want a converter on every SelectBooleanCheckbox object:
public Object getConvertedValue(FacesContext context,
UIComponent component, Object submittedValue)
throws ConverterException {
Converter converter = ((ValueHolder) component).getConverter();
if (converter == null) {
if(!(submittedValue instanceof String))
throw new ConverterException("Expecting submittedValue to be String");
else
return Boolean.valueOf((String)submittedValue);
}
return converter.getAsObject(context, component,
(String) submittedValue);
}
Otherwise you'll get a NullPointerException.
PS: There surely is a smarter way to achieve this information, but I am not smart enough. ;-)
You don't say whether you're using Hibernate but I assume that you must be for this to be an issue. Have you tried treating the numeric as a boolean in your mapping?
See this thread from the Hibernate forums

Should i use JSTL in JSF 2 xhtml pages?

I would like to bind a backing bean's field to the selected value of a selectOneListbox.
This value could be null, so i want to convert this to 0. This will set the selected value to the "default" selectItem. I'm using JSF2
I'm planning to do this with the http://java.sun.com/jstl/core taglib (using <c:if test="#{empty...}>)
My question is: is there a "cleaner" way to do this. Maybe JSF(2) related taglib?
Thankyou!
The "JSFish" way to do this would be to create a converter:
public Object getAsObject(FacesContext context, UIComponent comp, String param) {
return (param.equals("0")) ? null : param;
}
public String getAsString(FacesContext context, UIComponent comp, Object obj) {
return (obj == null) ? "0" : obj.toString();
}
Just use Long or Integer instead of String as item value. EL will automatically coerce number (and boolean) values from/to string.

Resources