Internationalization of multiple enums (translation of enum values) [duplicate] - java

This question already has answers here:
Localizing enum values in resource bundle
(5 answers)
Closed 7 years ago.
Again something that has been discussed before and where I wanted to share "my" solution and ask for enhancements, other approaches or best practices.
I have several enums where I need internationalization (I need the enum values translated into some languages in order to display them in a jsf page). Examle enum:
public enum TransferStatus {
NOT_TRANSFERRED,
TRANSFERRED
}
Translation would be for example Not yet transferred / Transferred, all good
The translation should be stored in a MessageBundle (properties files). I was searching for an easy, generic solution (best would be without the need of writing extra code in all the enums) that does not need much on the jsf side. Just to mention it, of course it it possible that two different enums shae the same enum value (e.g. values like COMPLETED that have a different meaning in different enums).
The solution I came up with:
(1) Store translations in the properties file like this:
TransferStatus.NOT_TRANSFERRED = Not yet transferred
TransferStatus.TRANSFERRED = Transferred, all good
(2) Make a helper class that takes an enum and generates the lookup key:
public class EnumTranslator {
public static String getMessageKey(Enum<?> e) {
return e.getClass().getSimpleName() + '.' + e.name();
}
}
(3) Add this code to every enum:
public String getKey() {
return EnumTranslator.getMessageKey(this);
}
(4) Now, I can access the translated values of my enums like this:
<h:outputText value="#{enum[order.transferStatus.key]}" />
Which is okay, but what I just don't like is adding the same getKey() method to every enum. There should be something better that that! Now it's your turn, SO :-)

Ok, now this is the complete and ready-to-use solution: (thanks to #Joop Eggen)
Make a class
public final class EnumTranslator {
public static String getMessageKey(Enum<?> e) {
return e.getClass().getSimpleName() + '.' + e.name();
}
}
Make it a custom EL function
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
version="2.0">
<namespace>http://example.com/enumi18n</namespace>
<function>
<function-name>xlate</function-name>
<function-class>your.package.EnumTranslator</function-class>
<function-signature>String getMessageKey(java.lang.Enum)</function-signature>
</function>
</facelet-taglib>
Add the taglib to your web.xml
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/enumi18n.taglib.xml</param-value>
</context-param>
Have properties files enum_en.properties and enum_yourlanguage.properties like this
TransferStatus.NOT_TRANSFERRED = Not transferred
TransferStatus.TRANSFERRED = Transferred
Add the properties files as resource bundles to your faces-config.xml
<resource-bundle>
<base-name>kk.os.obj.jsf.i18n.enum</base-name>
<var>enum</var>
</resource-bundle>
Add the custom taglib to your xhtml files
<html ... xmlns:l="http://example.com/enumi18n">
And - voilĂ  - you can now access the translated enum values in jsf:
<h:outputText value="#{enum[l:xlate(order.transferStatus)]}" />

I would make an EL function, leaving the enum classes as they are:
#{l:xlate(order.transferStatus)}
Okay, xlate has an Object parameter then (or Enum<?>). But the enum classes stay as they are.

The way I'd be doing this takes a bit more code (but far less than the other answers I've seen here), but I feel is more reliable:
#ManagedBean
#ApplicationScoped
public class EnumTranslator {
private <E extends Enum<E>> Map<E, String> getPresentableNames(Class<E> enumClass) {
ResourceBundle resources = ResourceBundle.getBundle(
"com.example.app.MyMessageBundle",
FacesContext.getCurrentInstance().getViewRoot().getLocale());
String prefix = enumClass.getSimpleName() + ".";
Map<E, String> map = new EnumMap<E, String>(enumClass);
for (E value : enumClass.getEnumConstants()) {
map.put(value, resources.getString(prefix + value));
}
return map;
}
// Bean method, accessible via EL
public Map<?, ?> getTransferStatuses() {
return getPresentableNames(TransferStatus.class);
}
}
Then your page can do:
<h:outputText value="#{enumTranslator.transferStatuses[order.transferStatus]}" />

What about:
public interface InternationalizationOfEnum {
default String getKey() {
return EnumTranslator.getMessageKey(this);
}
String name();}
and
public enum anyEnum implements InternationalizationOfEnum{

Related

How to get class public string in JSP? [duplicate]

How do you reference an constants with EL on a JSP page?
I have an interface Addresses with a constant named URL. I know I can reference it with a scriplet by going: <%=Addresses.URL%>, but how do I do this using EL?
EL 3.0 or newer
If you're already on Java EE 7 / EL 3.0, then the #page import will also import class constants in EL scope.
<%# page import="com.example.YourConstants" %>
This will under the covers be imported via ImportHandler#importClass() and be available as ${YourConstants.FOO}.
Note that all java.lang.* classes are already implicitly imported and available like so ${Boolean.TRUE} and ${Integer.MAX_VALUE}. This only requires a more recent Java EE 7 container server as early versions had bugs in this. E.g. GlassFish 4.0 and Tomcat 8.0.0-1x fails, but GlassFish 4.1+ and Tomcat 8.0.2x+ works. And you need to make absolutely sure that your web.xml is declared conform the latest servlet version supported by the server. Thus with a web.xml which is declared conform Servlet 2.5 or older, none of the Servlet 3.0+ features will work.
Also note that this facility is only available in JSP and not in Facelets. In case of JSF+Facelets, your best bet is using OmniFaces <o:importConstants> as below:
<o:importConstants type="com.example.YourConstants" />
Or adding an EL context listener which calls ImportHandler#importClass() as below:
#ManagedBean(eager=true)
#ApplicationScoped
public class Config {
#PostConstruct
public void init() {
FacesContext.getCurrentInstance().getApplication().addELContextListener(new ELContextListener() {
#Override
public void contextCreated(ELContextEvent event) {
event.getELContext().getImportHandler().importClass("com.example.YourConstants");
}
});
}
}
EL 2.2 or older
This is not possible in EL 2.2 and older. There are several alternatives:
Put them in a Map<String, Object> which you put in the application scope. In EL, map values are accessible the usual Javabean way by ${map.key} or ${map['key.with.dots']}.
Use <un:useConstants> of the Unstandard taglib (maven2 repo here):
<%# taglib uri="http://jakarta.apache.org/taglibs/unstandard-1.0" prefix="un" %>
<un:useConstants className="com.example.YourConstants" var="constants" />
This way they are accessible the usual Javabean way by ${constants.FOO}.
Use Javaranch's CCC <ccc:constantsMap> as desribed somewhere at the bottom of this article.
<%# taglib uri="http://bibeault.org/tld/ccc" prefix="ccc" %>
<ccc:constantsMap className="com.example.YourConstants" var="constants" />
This way they are accessible the usual Javabean way by ${constants.FOO} as well.
If you're using JSF2, then you could use <o:importConstants> of OmniFaces.
<html ... xmlns:o="http://omnifaces.org/ui">
<o:importConstants type="com.example.YourConstants" />
This way they are accessible the usual Javabean way by #{YourConstants.FOO} as well.
Create a wrapper class which returns them through Javabean-style getter methods.
Create a custom EL resolver which first scans the presence of a constant and if absent, then delegate to the default resolver, otherwise returns the constant value instead.
The following does not apply to EL in general, but instead to SpEL (Spring EL) only (tested with 3.2.2.RELEASE on Tomcat 7).
I think it is worth mentioning it here in case someone searches for JSP and EL (but uses JSP with Spring).
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<spring:eval var="constant" expression="T(com.example.Constants).CONSTANT"/>
You usually place these kinds of constants in a Configuration object (which has getters and setters) in the servlet context, and access them with ${applicationScope.config.url}
You can't. It follows the Java Bean convention. So you must have a getter for it.
I'm defining a constant in my jsp right at the beginning:
<%final String URI = "http://www.example.com/";%>
I include the core taglib in my JSP:
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
Then, I make the constant available to EL by following statement:
<c:set var="URI" value="<%=URI%>"></c:set>
Now, I can use it later. Here an example, where the value is just written as HTML comment for debugging purposes:
<!-- ${URI} -->
With your constant class, you can just import your class and assign the constants to local variables. I know that my answer is a sort of quick hack, but the question also bumps up when one wants to define constants directly in the JSP.
I implemented like:
public interface Constants{
Integer PAGE_SIZE = 20;
}
-
public class JspConstants extends HashMap<String, String> {
public JspConstants() {
Class c = Constants.class;
Field[] fields = c.getDeclaredFields();
for(Field field : fields) {
int modifier = field.getModifiers();
if(Modifier.isPublic(modifier) && Modifier.isStatic(modifier) && Modifier.isFinal(modifier)) {
try {
Object o = field.get(null);
put(field.getName(), o != null ? o.toString() : null);
} catch(IllegalAccessException ignored) {
}
}
}
}
#Override
public String get(Object key) {
String result = super.get(key);
if(StringUtils.isEmpty(result)) {
throw new IllegalArgumentException("Check key! The key is wrong, no such constant!");
}
return result;
}
}
Next step put instance of this class into servlerContext
public class ApplicationInitializer implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent sce) {
sce.getServletContext().setAttribute("Constants", new JspConstants());
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
add listener to web.xml
<listener>
<listener-class>com.example.ApplicationInitializer</listener-class>
</listener>
access in jsp
${Constants.PAGE_SIZE}
Static properties aren't accessible in EL. The workaround I use is to create a non-static variable which assigns itself to the static value.
public final static String MANAGER_ROLE = 'manager';
public String manager_role = MANAGER_ROLE;
I use lombok to generate the getter and setter so that's pretty well it. Your EL looks like this:
${bean.manager_role}
Full code at https://rogerkeays.com/access-java-static-methods-and-constants-from-el
Yes, you can. You need a custom tag (if you can't find it somewhere else). I've done this:
package something;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import org.apache.taglibs.standard.tag.el.core.ExpressionUtil;
/**
* Get all class constants (statics) and place into Map so they can be accessed
* from EL.
* #author Tim.sabin
*/
public class ConstMapTag extends TagSupport {
public static final long serialVersionUID = 0x2ed23c0f306L;
private String path = "";
private String var = "";
public void setPath (String path) throws JspException {
this.path = (String)ExpressionUtil.evalNotNull ("constMap", "path",
path, String.class, this, pageContext);
}
public void setVar (String var) throws JspException {
this.var = (String)ExpressionUtil.evalNotNull ("constMap", "var",
var, String.class, this, pageContext);
}
public int doStartTag () throws JspException {
// Use Reflection to look up the desired field.
try {
Class<?> clazz = null;
try {
clazz = Class.forName (path);
} catch (ClassNotFoundException ex) {
throw new JspException ("Class " + path + " not found.");
}
Field [] flds = clazz.getDeclaredFields ();
// Go through all the fields, and put static ones in a Map.
Map<String, Object> constMap = new TreeMap<String, Object> ();
for (int i = 0; i < flds.length; i++) {
// Check to see if this is public static final. If not, it's not a constant.
int mods = flds [i].getModifiers ();
if (!Modifier.isFinal (mods) || !Modifier.isStatic (mods) ||
!Modifier.isPublic (mods)) {
continue;
}
Object val = null;
try {
val = flds [i].get (null); // null for static fields.
} catch (Exception ex) {
System.out.println ("Problem getting value of " + flds [i].getName ());
continue;
}
// flds [i].get () automatically wraps primitives.
// Place the constant into the Map.
constMap.put (flds [i].getName (), val);
}
// Export the Map as a Page variable.
pageContext.setAttribute (var, constMap);
} catch (Exception ex) {
if (!(ex instanceof JspException)) {
throw new JspException ("Could not process constants from class " + path);
} else {
throw (JspException)ex;
}
}
return SKIP_BODY;
}
}
and the tag is called:
<yourLib:constMap path="path.to.your.constantClass" var="consts" />
All public static final variables will be put into a Map indexed by their Java name, so if
public static final int MY_FIFTEEN = 15;
then the tag will wrap this in an Integer and you can reference it in a JSP:
<c:if test="${consts['MY_FIFTEEN'] eq 15}">
and you don't have to write getters!
You can. Try in follow way
#{T(com.example.Addresses).URL}
Tested on TomCat 7 and java6
Even knowing its a little late, and even knowing this is a little hack - i used the following solution to achieve the desired result. If you are a lover of Java-Naming-Conventions, my advice is to stop reading here...
Having a class like this, defining Constants, grouped by empty classes to create kind of a hierarchy:
public class PERMISSION{
public static class PAGE{
public static final Long SEE = 1L;
public static final Long EDIT = 2L;
public static final Long DELETE = 4L;
...
}
}
can be used from within java as PERMISSION.PAGE.SEE to retrieve the value 1L
To achieve a simliar access-possibility from within EL-Expressions, I did this:
(If there is a coding-god - he hopefully might forgive me :D )
#Named(value="PERMISSION")
public class PERMISSION{
public static class PAGE{
public static final Long SEE = 1L;
public static final Long EDIT = 2L;
public static final Long DELETE = 4L;
...
//EL Wrapper
public Long getSEE(){
return PAGE.SEE;
}
public Long getEDIT(){
return PAGE.EDIT;
}
public Long getDELETE(){
return PAGE.DELETE;
}
}
//EL-Wrapper
public PAGE getPAGE() {
return new PAGE();
}
}
finally, the EL-Expression to access the very same Long becomes: #{PERMISSION.PAGE.SEE} - equality for Java and EL-Access. I know this is out of any convention, but it works perfectly fine.
#Bozho already provided a great answer
You usually place these kinds of constants in a Configuration object (which has getters and setters) in the servlet context, and access them with ${applicationScope.config.url}
However, I feel an example is needed so it brings a bit more clarity and spare someone's time
#Component
public Configuration implements ServletContextAware {
private String addressURL = Addresses.URL;
// Declare other properties if you need as also add corresponding
// getters and setters
public String getAddressURL() {
return addressURL;
}
public void setServletContext(ServletContext servletContext) {
servletContext.setAttribute("config", this);
}
}
There is a workaround that is not exactly what you want, but lets you active almost the same with touching scriptlets in a quite minimal way. You can use scriptlet to put value into a JSTL variable and use clean JSTL code later in the page.
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%# page import="com.whichever.namespace.Addresses" %>
<c:set var="ourUrl" value="<%=Addresses.URL%>"/>
<c:if test='${"http://www.google.com" eq ourUrl}'>
Google is our URL!
</c:if>

enuma label from message bundle

I have a enum with some entries for a selectOneMenu, that means the enum stucture looks like this: display, pdfLabel.
I want to load the entries label from my message bundle, that means depending on the locale.
It works fine, but only the first time after I depoly the project. That means, if the locale is "en" first time I load the entries, even after logout - session invalidate; if I change the locale to "de" the entries are still from the "en" - message. It works only if I redeploy.
Anyone has an idea about this behavior?
My enum:
public enum Transportmittel {
TRUCK(I18n.get("tv.moc.truck"), "TRUCK"),
AIRFREIGHT(I18n.get("tv.moc.airfreight"), "AIRFREIGHT"),
TRAIN(I18n.get("tv.moc.train"), "TRAIN"),
SEAFREIGHT(I18n.get("tv.moc.seafreight"), "SEAFREIGHT"),
BARGE(I18n.get("tv.moc.barge"), "BARGE");
String ausgabe;
String pdfLabel;
private Transportmittel(String ausgabe, String pdfLabel) {
this.ausgabe = ausgabe;
this.pdfLabel = pdfLabel;
}
public String toString() {
return ausgabe;
}
public String getLabelForPdf() {
return pdfLabel;
}
}
The controller where I load the entries:
#PostConstruct
public void init() {
transportMittelSelectList.add(new SelectItem(Transportmittel.TRUCK.pdfLabel, Transportmittel.TRUCK.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.TRAIN.pdfLabel, Transportmittel.TRAIN.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.AIRFREIGHT.pdfLabel, Transportmittel.AIRFREIGHT.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.SEAFREIGHT.pdfLabel, Transportmittel.SEAFREIGHT.ausgabe));
transportMittelSelectList.add(new SelectItem(Transportmittel.BARGE.pdfLabel, Transportmittel.BARGE.ausgabe));
}
And this is where I load the message bundle:
public class I18n {
public static String get(String msg) {
FacesContext context = FacesContext.getCurrentInstance();
ResourceBundle bundle = context.getApplication().getResourceBundle(
context, "messages");
return bundle.getString(msg);
}
}
The enum-values are static - so their constructor is called only once when loading the class by the classloader (=the first use). So at consecutive uses you still use the same instance containing the same string ausgabe set at construction-time during the first use.
So you have to set the values for ausgabe and pdfLabel when it is used. But maybe it is even better to have some "external" class which knows how to get the different labels for your enum-value instead of having these values somehow hard-coded inside the enum.
This is indeed not going to work. Enum properties are initialized only once, applicationwide, while i18n is essentially supposed to be resolved on a per-request basis.
You need to redesign your enum as such that only the label keys are hold instead of the resolved localized values.
TRUCK("tv.moc.truck", "TRUCK"),
AIRFREIGHT("tv.moc.airfreight", "AIRFREIGHT"),
TRAIN("tv.moc.train", "TRAIN"),
SEAFREIGHT("tv.moc.seafreight", "SEAFREIGHT"),
BARGE("tv.moc.barge", "BARGE");
And then provide the enum values as follows in an application scoped bean:
#ManagedBean
#ApplicationScoped
public class Data {
public Transportmittel[] getTransportmittels() {
return Transportmittel.values();
}
}
And then reference it in <f:selectItems> as follows (look, no need for SelectItem boilerplate):
<f:selectItems value="#{data.transportmittels}" var="transportmittel"
itemValue="#{transportmittel}" itemLabel="#{bundle[transportmittel.ausgabe]}" />
Or, if you happen to use JSF utility library OmniFaces already, as currently indicated in your user profile, then you could also bypass the whole application scoped Data bean and import it straight in the EL scope as follows:
<o:importConstants type="com.example.Transportmittels" /> <!-- can be declared in a master template -->
...
<f:selectItems value="#{Transportmittels}" var="transportmittel"
itemValue="#{transportmittel}" itemLabel="#{bundle[transportmittel.ausgabe]}" />
See also:
Localizing enum values in resource bundle
I had the same problem, but with ZK, I did need to fetch some properties to my enum, but it was blank String everytime.
To solve this you need to pass as the arguments the key of your property file in your enum constructor, like this:
After that in the get method of your enum propertie you must get the values in resource bundle and return them, like this:

Extending XML validation / auto-completion

I have several types of data-sources, that I would like to use for additional XML validation and providing auto-completion (using Eclipse if possible).
This source could be some other XML (from another or the same file):
<type name="TypeA"/>
<type name="TypeB"/>
or a Java-class
public List<String> getValues() {
return Arrays.asList("Val1", "Val2", "Val3");
}
These values are then referenced in other XML-files:
<x type="TypeA" value="Val2" />
<x type="TypeB" value="Val3" />
Now I would like to improve editing this file by
Validating the XML-File
(underline wrong types/values, if possible display a red x in Package Expl.)
Providing code-completion
(suggest TypeA and TypeB when typing type=")
I'll certainly have to write some code, but what is the best way start?
Can the standard XML-Editor be extended?
Are there any plugins that can help? (Maybe Rinzo XML Editor?)
Any other options that I did not think of?
You can write XSD schemas for your XML files, then Eclipse can validate them.
There are, for sure, frameworks who generate XSD schemas from your Java classes.
Check the answers here: utility to generate xsd from java class
If you decide extending Rinzo, it seems documentation has bee updated on how to extend the same features you'll like to customize :)
http://editorxml.sourceforge.net/extendingRinzo.html
Peter,
I answer you in a new post since I didn't have enough space in a comment.
If you want to extend Rinzo according to your example I guess you'll need to create a plugin contributing the extension points declared in the site's documentation.
For the content assistant implementation I guess a rough implementation based on your examples could be as follow:
public class CustomSourceAssistProcessor implements IXMLContentAssistProcessor {
#Override
public void addAttributeValuesProposals(XMLNode currentNode, String attributeName, String prefix,
ITextViewer viewer, int offset, Collection<ICompletionProposal> results) {
if("x".equals(currentNode.getTagName()) && "type".equals(attributeName)) {
for (String possibleValue : this.getPossibleValuesFromXML()) {
results.add(new CompletionProposal(possibleValue, offset, prefix.length(), 0, null, "Proposal Description...", null, null));
}
}
if("x".equals(currentNode.getTagName()) && "value".equals(attributeName)) {
for (String possibleValue : this.getPossibleValuesFromJavaClass()) {
results.add(new CompletionProposal(possibleValue, offset, prefix.length(), 0, null, "Proposal Description...", null, null));
}
}
}
}
That's as far as interacting with Rinzo's API, and your particular logic to gather values either from an external XML file or java-class should be implemented in the methods getPossibleValuesFromXML() and getPossibleValuesFromJavaClass()
On the other hand in order to add your custom validator I guess the rough implementation of your extension point, also based on your example, should be similar to this one:
public class CustomSourceXMLValidator implements XmlValidator {
#Override
public void validate(RinzoXMLEditor editor) {
editor.getModel().getTree().accept(new HierarchicalVisitor() {
#Override
public boolean visitStart(XMLNode node) {
if(node.isTag() && "x".equals(node.getTagName())) {
for (Entry<String, XMLAttribute> entry : node.getAttributes().entrySet()) {
if("type".equals(entry.getKey())) {
this.validateValueFromXML(entry.getValue().getValue());
}
if("value".equals(entry.getKey())) {
this.valdateValueFromJavaClass(entry.getValue().getValue());
}
}
}
return true;
}
private void valdateValueFromJavaClass(XMLAttribute xmlAttribute) {
if(!this.getPossibleValuesFromXML().contains(xmlAttribute.getValue())) {
this.createMarker(editor, xmlAttribute);
}
}
private void validateValueFromXML(XMLAttribute xmlAttribute) {
if(!this.getPossibleValuesFromJavaClass().contains(xmlAttribute.getValue())) {
this.createMarker(editor, xmlAttribute);
}
}
});
}
}
And once again it's up to you the implementation of the methods getPossibleValuesFromXML() and getPossibleValuesFromJavaClass().
You can also see the source code of ClassNamesValidatorVisitor as an example.
Keep on rockin' in the free world! :)
I'm pretty sure eclipse already does both these things but they are part of one of the extended packages. Try downloading eclipse for Java EE developers. I'm fairly sure validation and completetion are part of the Web Tools Platform.
Check Here For Validating XML

Trimming inputs in JBoss Seam

I am making a web application using JBoss Seam 2.2.0, and I want to trim my inputs before receiving them, even before the Hibernate Bean Validation phase. Is this possible?
I saw someone using a PhaseListener to do the same functionality. Is this the best way to do it?
I use a Converter for this. Works very well.
Page:
<h:inputText value="#{myBean.myValue}" converter="#{stringTrimConverter}"/>
Code:
#Name("stringTrimConverter")
#BypassInterceptors
#Converter
public class StringTrimConverter implements javax.faces.convert.Converter {
public Object getAsObject(FacesContext context, UIComponent cmp, String value) {
if(StringUtils.isBlank(value)) {
return null;
} else {
return value;
}
}
public String getAsString(FacesContext context, UIComponent cmp, Object value) {
if(value != null) {
return value.toString().trim();
}
return null;
}
}
One suggestion is to trim the text in Javascript, once the user changes the value of the input:
<h:inputText ... onchange="this.value = trim(this.value);"/>
with the Javascript function:
function trim(myString) {
return myString.replace(/^\s+/g,'').replace(/\s+$/g,'');
}
Edit, regarding your comment:
The solution I suggested is the best way to do that because it is done on the client side. However, as you said in the comment, it will not work if the client's browser does not allow Javascript.
As shown here, 95% of the users activate the Javascript on their browsers (and it was in January 2008 !).
However, if you really need to support none Javascript browsers, I suggest that you indeed implement the PhaseListener solution.
Edit2
The solution proposed by Damo with a Convertor is also working, but you will need to specify the converter for every input, which is not needed with the PhaseListener, i.e. you will need to always add converter="#{stringTrimConverter}" for all of your inputs.
Perhaps a better way is to extend the <h:inputText>, create your own component that is pretty much the same as <h:inputText> but that trimmes the result by default.
In my opinion though, there should be an attribute in inputText that trimmed by default ie:
<h:inputText value="#{myBean.text}" trim="true"/>
Update:
Ok, so here is how you can create a component that trim's the inputText fields.
Note, however that I haven't tested the code, so I am not 100% sure it will work, but it should.
In faces-config.xml
Add your component
<component>
<component-type>foo.InputControlComponent</component-type>
<component-class>my.package.foo.InputControl</component-class>
</component>
Create WEB-INF/foo.taglib.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE facelet-taglib PUBLIC
"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
"http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://whatever.com/foo</namespace>
<tag>
<tag-name>inputControl</tag-name>
<component>
<component-type>foo.InputControlComponent</component-type>
</component>
</tag>
</facelet-taglib>
In web.xml
<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/foo.taglib.xml</param-value>
</context-param>
InputControl.java
public class InputControl extends UIPanel {
public InputControl() {
super();
}
private void childrenEncodeBegin(FacesContext context, List<UIComponent> children) {
for (UIComponent comp : children) {
if (comp instanceof UIInput) {
comp = (UIInput) comp;
((UIInput) comp).setValue(((UIInput) comp).getValue().toString().trim());
}
// Encode recursively
if (comp.isRendered() && comp.getChildCount() > 0)
childrenEncodeBegin(context, comp.getChildren());
}
}
public void encodeBegin(FacesContext context) throws IOException {
if (getChildren() != null)
childrenEncodeBegin(context, getChildren());
}
}
Now in your xhtml you can use it like this:
<foo:inputControl>
<ui:include src="myForm.xhtml"/>
</foo:inputControl>

Is it possible overload an EL method in JSF 1.1 / Facelets

Is it possible to overload an EL method in JSF 1.1 using Facelets as your view handler? If so, how?
For example, I have this code defining my EL methods (which are defined in namespace k):
public static String doStuff( String s ) {
return doStuff( null, s );
}
public static String doStuff( Map<String,String> m, String s ) {
...
return something;
}
When I try to call #{k:doStuff("hey!")} from my Facelets page, I get this error:
Function 'k:doStuff' specifies 2 params, but 1 was declared
It turns out that no matter how you declare the function, it is being put in a Map with its name used as a key. So - no function overloading.
However, you can define the name of the function in the XML to be different, and then you can have overloaded methods. It's a bit counter-intuitive. But then your functions will be accessible via different names in the pages.
You can achieve the same thing dynamically, by supplying suffixes to overloaded method names (which you put in the map). Either way it's not such a good solution.
It looks like the problem was with how it was declared. For example, I was using this to declare my methods:
public class KTagLib extends AbstractTagLibrary {
public static final String NAMESPACE = "http://mysite.blah/tags";
public static final KTagLib INSTANCE = new KTagLib();
public KTagLib() {
super(NAMESPACE);
try{
try {
Method[] methods = KTags.class.getMethods();
for (int i = 0; i < methods.length; i++) {
if (Modifier.isStatic(methods[i].getModifiers())) {
this.addFunction(methods[i].getName(), methods[i]);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
and using the following configuration:
<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
<library-class>mypackage.KTagLib</library-class>
</facelet-taglib>
However, the this.addFunction() is essentially calling put() on a java.util.Map object so that duplicate methods can't be added since the keys are the same between doStuff.
To solve this problem, I'll have to explicitly declare the methods in the *.taglib.xml unless anyone knows of a way to dynamically solve the problems.

Categories