tags:

views:

1163

answers:

4

Hello guys,

I need to use function with argument in a EL expression (with JSF) like this:

<h:outputText value="#{object.test(10)}" ></h:outputText>

But it doesn't work.

I read on the web that it's impossible to do this with JSF. I use facelet with JSF.

Someone knows how to do that ?

Thanks.

A: 

You may have to have <%@ page isELIgnored ="false" %> at the top of your pages. Read more here. The default is to ignore el expressions. What version of the JSP spec are you using with JSF? If you are using JSF 2 with JSP < 2.1 you are going to run into problems.

Also, what version of el are you using? You can't pass method params with older versions.

    <dependency>
  <groupId>javax.el</groupId>
  <artifactId>el-api</artifactId>
  <version>2.1.2-b05</version>
 </dependency>
amischiefr
With facelets, they aren't using JSP at all.
digitaljoel
@digital - Yes, this is true. However, you can mix and match and use both JSP with Facelets in your application. The OP doesn't specify whether this is the case or not.
amischiefr
+1  A: 

I find a sad solution but it's working. I overload a map like this:

new AbstractMap<Integer, String>()
    {

        @Override
        public Set<Entry<Integer, String>> entrySet()
        {
            return null;
        }

        @Override
        public String get(final Object arg0)
        {
            Integer keywordDb = (Integer)arg0;
            GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
            HashMap<String, String> params = new HashMap<String, String>();
            params.put("keywordDb", keywordDb.toString());
            params.put("month", new Integer(cal.get(Calendar.MONTH) + 1).toString());
            params.put("year", new Integer(cal.get(Calendar.YEAR)).toString());
            DataAnalyzeManager manager = new DataAnalyzeManager();
            manager.setEm(modelPosition.getEm());
            DataAnalyze data = manager.findDataByParams(params, modelPosition.getSite(), false, DataAnalyzeManager.VISITBYMONTHBYKEYWORD);
            if (data != null)
                return data.getDataInt().toString();
            return "0";
        }
    };

Thereby, I can do that in my JSF:

#{homePositionController.visitByMonth[keyword.keyword.keywordDb]}

And my function is executed.

Kiva
+3  A: 

You could provide the method as a custom facelet function in your own taglib. The method must be static, so if you are trying to call a method on a specific bean, you would have to pass the bean, and the parameters to your static facelet function. In your case, it would be something like

<h:outputText value="#{my:doStuff(object,10)}" ></h:outputText>

and your facelet function would be

public static String doStuff( MyType o, int param )
{
    return o.test( param );
}

Then, using the information in the facelets docbook you would define your function in your taglib.xml file.

It's not the prettiest solution, especially if you plan on doing this a lot, but I believe the next version of the EL (in java EE 6) will allow for using parameters in some cases.

Edit: Some info about parameterized method calls in the next version of el can be found on Ryan Lubke's Blog

digitaljoel
A: 

There is couple ways about doing that, you could use JBoss EL expression implementation they support method calls with parameters check out Seam, or use similar approach as @digitaljoel suggested. This is what I created for that purpose, you can call static and static methods, not a great solution but it does the job.

<c:if  test="#{t:call(null, '@Util.SecurityUtility', 'isPanelWorkbookEnabledForUser','')}">
      Hello Panel    
  </c:if>

@Util is just an alias to com.mycomp.util where

Example 2

<c:if test="#{item != null and  t:call(item, 'java.lang.String', 'indexOf', t:params(t:param('flash-alert',''))) == 0}">                
    #{t:call(session, 'org.apache.catalina.session.StandardSessionFacade', 'removeAttribute', t:params(t:param(item,'')))}      
</c:if>

Syxtax java.lang.Object call(java.lang.Object, java.lang.String, java.lang.String, java.lang.Object[]) Where Object is object we want to invoke method on, String is the method name, Object[] are parameters to pass.

t:call, t:params, t:param are function defined in project-taglib.xml as so

    <function>
 <function-name>call</function-name>
 <function-class>util.Functions</function-class>
 <function-signature>java.lang.Object call(java.lang.Object, java.lang.String, java.lang.String, java.lang.Object[])</function-signature>
</function>
<function>
 <function-name>param</function-name>
 <function-class>.util.Functions</function-class>
 <function-signature>java.lang.String param(java.lang.Object, java.lang.String)</function-signature>
</function> 

<function>
 <function-name>params</function-name>
 <function-class>util.Functions</function-class>
 <function-signature>java.lang.Object[] params(java.lang.String)</function-signature>
</function>

Here is the implementation

package mycompany.web.util;

import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Date; import java.util.HashMap; import java.util.List;

import javax.el.MethodNotFoundException;

public class Functions {

private  static HashMap<String, String> alliasMap;
static{
 alliasMap=new HashMap<String, String>();
 alliasMap.put("@DateUtil", "com.americanbanksystems.compliance.util.DateUtil");
 //Match anything following the dot(.)
 alliasMap.put("@Util.*", "com.americanbanksystems.compliance.util");

 alliasMap.put("@Application.*", "com.americanbanksystems.compliance.application");

}




public static String param(Object obj, String cls) { 
 //make sure that passed in object is not null
 if(obj==null){
  obj="";
 }

 ByteArrayOutputStream baut=new ByteArrayOutputStream();
 XMLEncoder encoder=new XMLEncoder( baut );
 //Bug in the JDK
 //http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=c993c9a3160fd7de44075a2a1fa?bug_id=6525396
 if(obj instanceof java.sql.Timestamp){
  Date o = new Date(((java.sql.Timestamp)obj).getTime());
  obj=o;
 }  
 //Checking if this is possible 
 if(String.class.isAssignableFrom(obj.getClass())){
  //removed trailing +" " because it was causing indexOf return invalid value
  //Unknown side effects
  obj=FacesUtil.get(obj.toString());   
 }
  encoder.writeObject( obj );
 encoder.close();
 return new String(baut.toByteArray());
}

private static Object decode(String str){
 ByteArrayInputStream bais=new ByteArrayInputStream(str.getBytes());
 XMLDecoder decoder=new XMLDecoder(bais);
 return decoder.readObject();
}

public static Object[] params(String str){
 // (?<=</java>)\s*(?=<?)
 String[] obj=str.split("(?<=</java>)\\s*(?=<?)");
 Object[] results=new Object[obj.length];
 for(int i=0;i<obj.length;i++){
  results[i]=decode(obj[i]);
 }
 return results;
}


@SuppressWarnings("unchecked")
public static Object call(Object owningObject, String qualifiedClassname, String methodName, java.lang.Object... methodArguments) {
 if (null == methodName || methodName.equals("")) {
  throw new IllegalArgumentException("Method name can't be null or empty");
 }
 if (null == methodArguments) {
  methodArguments = new Object[0];
 }

 //Check for aliases 
 if(qualifiedClassname.indexOf("@")>-1){
  String subpackage=qualifiedClassname;
  String originalClass=qualifiedClassname;
  //Split at the dot
  boolean isPackageAllias=false;
  String[] sp=subpackage.split("\\."); 
  if(sp.length>1){
   subpackage=sp[0]+".*";
   isPackageAllias=true;
  }
  if(alliasMap.containsKey(subpackage)){
   String value = alliasMap.get(subpackage);
   if(isPackageAllias){
    qualifiedClassname=subpackage.replace(sp[0], value);
    qualifiedClassname=qualifiedClassname.replace(".*", originalClass.replace(sp[0],""));
   }else{
    qualifiedClassname=value;
   }
  }else{
   throw new IllegalArgumentException("Allias name '"+qualifiedClassname+"' not found");
  }
 }
 Class clazz;
 try {
  clazz = Class.forName(qualifiedClassname);
  //Find method by methodName,Argument Types
  Class[] argumentTypes=new Class[methodArguments.length]; 

  for(int i=0;i<methodArguments.length;i++){
   argumentTypes[i]=methodArguments[i].getClass();
   //Check if the passed in method argument is a string and if its represented as unicode char    
   //if it is then convert it into a char and reassign to the original parameter
   //example 1:  \u0022 == "
   //example 2:  \u0027 == '
   // Reason for this functionality is that we can't pass " and ' from within t:call method
   if (argumentTypes[i] == String.class && methodArguments[i].toString().indexOf("\\u") > -1) {
    String arg = methodArguments[i].toString();
    arg = arg.substring(2, arg.length());
    try {
     int outchar = Integer.parseInt(arg, 16);
     if (Character.isDefined(outchar)) {
      methodArguments[i] = String.valueOf((char) outchar);
     }
    } catch (NumberFormatException nfe) {
     // Suppress error and continue assuming this is a regular string
    }
   }
  }

  Method methodToInvoke = null;
  try{
   methodToInvoke  = clazz.getMethod(methodName, argumentTypes);
  }catch(NoSuchMethodException nsm){//Find by method name/ argument count
   for (Method method : clazz.getMethods()) {
    if (method.getName().equals(methodName)  && method.getParameterTypes().length == methodArguments.length) {
     if (null == owningObject) {
      owningObject = clazz.newInstance();
     }
     methodToInvoke=method;
     break;
    }
   }
  }

  if(methodToInvoke!=null){        
   return methodToInvoke.invoke(owningObject, methodArguments);
  }else{
   throw new InstantiationException("method not found :" + methodName); 
  }

 } catch (ClassNotFoundException e) {
  e.printStackTrace();
 } catch (IllegalArgumentException e) {
  e.printStackTrace();
 } catch (IllegalAccessException e) {
  e.printStackTrace();
 } catch (InvocationTargetException e) {
  e.printStackTrace();
 } catch (InstantiationException e) {
  e.printStackTrace();
 }
 return null;
}


public static void main(String[] arg) {
 // StringBuffer buff=new StringBuffer();
 // buff.append("Gregs init");
 // Functions.call(java.lang.Class<T>, T, java.lang.String, java.lang.String, java.lang.Object...)
 /*
  * Functions.call(StringBuffer.class, buff, "java.lang.StringBuffer","append"," Init ");
  * Functions.call(StringBuffer.class, buff, "java.lang.StringBuffer","append"," greg ");
  * System.out.println("output="+ buff);
  */

 //#{t:call(null, ".util.DateUtil", "normalizeDate", t:parametize(editRiskActionPlan.riskActionPlan.completionDate,",","java.lang.Object"))}
// c(call(null, "util.DateUtil", "normalizeDate", new Date()));

 // #{t:parametize(editRiskActionPlan.riskActionPlan.completionDate,",","java.lang.Object")}
 //parametize((new Date()).toString(),",","java.lang.Object");
 Date a=new Date();

 Date b=new Date();

 String rawString=param((Date)b, Date.class.toString() );     
 //System.out.println(rawString);

 //Replaced=#{t:call("Gregs ' car", 'java.lang.String', 'replace', t:params( parameter ))}

 String paramA=param("\\u0027","");
 String paramB=param("\\u0022","");
 String params=paramA+paramB;
 String in="I need to ' have a replaced single quote with double";
 String out=(String)call(in, "java.lang.String", "replace", params(params));

 System.out.println(out);


 /*
 Object[] obj=params(rawString);
 for(Object o:obj){
  System.out.println(o);
 }
 //c(call(null, "@DateUtil", "normalizeDate", obj));

 */

}

}

I hope this helps, btw this was copied/pasted from my project so not sure if I missed anything.

Greg