Problems calling Java methods in velocity template with Jira - java

I´m developing a plugin for jira which contains a custom field type.
The field is a select and has to be filled with options. I supply these options via the Jira method getVelocityParameters().
#Override
public Map<String, Object> getVelocityParameters(Issue issue,
CustomField field, FieldLayoutItem fieldLayoutItem) {
Map<String, Object> map = super.getVelocityParameters(issue, field, fieldLayoutItem);
map.put("customOptions", getCustomOptions());
return map;
}
getCustomOptions() returns a Hashtable with the Options i need.
To access and display these options i used a #foreach loop in the template:
#foreach($customOption in $customOptions)
<option id="$customOption.Id" value="$customOption.Value">
$customOption.Label
</option>
#end
Instead of showing the returned Objects it simply just display the text itself, only the "$customOption.Id" is displayed correctly.
And writing only "$customOption" shows the whole reference to the object.
So i CAN access the object and its id but not the other properties.
Id is an int, while label and value are Strings.
i searched for solutions and tried different things to solve this problem, e.g.:
$!customOption.Label, ${!customOption.Label}, ${customOption.Label}, $customOption.getLabel()
I can´t find the problem here, because the id is working properly.
Sry for the broken english.

Because you use Map. So try the following:
#foreach($customOption in $customOptions)
#if ($customOption)
#foreach ($co in $customOption.keySet())
$customOption[$co]
#end
#end
#end

Velocity will display the source if there is no value for your issue.
E.g. if you want to obtain a customfield value, you should check the value and if its not set you can either load the default value or just ignore it.

I think you should look into the fact that Velocity will only display a field value if its class has a public get method for that field.
Say customOption is an object of class X, then class X must have a public get() method that returns the label.
It does not matter if the label field is a public field of class X, public get() method is necessary.
You can have a look at this for reference:

Related

How to get the value from a Form without validating the fields?

I have a scala template and I've passed a form instance with a Ticket instance populated with my data in order to render my page ticketForm : Form[Ticket]. However, part of my template renders a List<Object> items into a <ul> and I don't want to pass this list as a parameter on the template as I already have it as a property on the ticket object itself. I was using ticketForm.get().getItems but it runs the validation and throws an exception.
Is there any another way to do that?
Do not use get, just ticketForm('myproperty')
An example of generation radio group:
https://github.com/playframework/playframework/blob/master/framework/src/play/src/main/scala/views/helper/inputRadioGroup.scala.html
In the case, if you want to process a list, like the #repeat helper
#helper.repeat(userForm("emails"), min = 1) { emailField =>
#helper.inputText(emailField)
}
Here is the realization of the repeat helper:
https://github.com/playframework/playframework/blob/master/framework/src/play/src/main/scala/views/helper/Helpers.scala#L80

Freemarker and Struts 2, sometimes it evaluates as a sequence+extended_hash

First let me say that using Struts2 + Freemarker is a real blast.
Yet there's something is driving me crazy, because I cannot understand why it happens. I ask here as maybe someone else has an idea to share about it.
I've got an action, with a property.
Say
private String myText;
Then I've got a setter and a getter:
public void setMyText(String myText)
{
this.myText = myText;
}
public String getMyText()
{
if (myText == null)
myText = "(empty)";
return this.myText;
}
The result (in struts.xml) is a freemarker result.
So in my Freemarker template there's a line like the following:
<p>The text is: ${myText}</p>
Now consider I'm calling the action without any text parameter: say the url is
http:localhost:8080/myapp/myaction
As the getter provides a default value, when the action is processed and the result passed to my template, the property is set to the default; so I get (html on the browser side)
<p>The text is: (empty)</p>
If I call my action with the parameter set, instead (I mean with something like:
http:localhost:8080/myapp/myaction?myText=hallo
) things go wrong. Freemarker fires the following exception:
Exception occurred during processing request: For "${...}" content:
Expected a string or something automatically convertible to string
(number, date or boolean), but this has evaluated to a
sequence+extended_hash (String[] wrapped into f.e.b.ArrayModel)
It seems that "myText" is found twice...
What am I doing wrong? Or, at least, is there anyone that can explain to me why it happens?
P.S.: it's really found twice; the following is a way to workaround the problem:
<#if myText?is_sequence>${myText[0]}<#else>${myText}</#if>
Yet it seems to me not viable to wrap every variable in that way.
P.P.S.: a further hint: in the freemarker template there's a call to another action some lines before. Something like:
<#s.action var="innerAction" name="getTable" namespace="/foo" />
If I comment the line above, everything works fine.
The myText could be a variable from the freemarker context, but if you want to use action property
<p>The text is: ${action.myText}</p>
Note, that action prefix is not required to access action properties. A property resolution method is applied when resolving freemarker variables:
Property Resoloution:
Your action properties are automatically resolved - just like in a
velocity view.
for example ${name} will result in stack.findValue("name"), which
generally results in action.getName() being executed.
A search process is used to resolve the variable, searching the
following scopes in order, until a value is found :
freemarker variables
value stack
request attributes
session attributes
servlet context attributes
And later you can read what objects are accessible from the context.
Objects in the Context:
The following variables exist in the FreeMarker views
req - the current HttpServletRequest
res - the current HttpServletResponse
stack - the current OgnlValueStack
ognl - the OgnlTool instance
This class contains useful methods to execute OGNL expressions against arbitary objects, and a method to generate a select list using
the <s:select> pattern. (i.e. taking the name of the list property, a
listKey and listValue)
struts - an instance of StrutsBeanWrapper
action - the current Struts action
exception - optional the Exception instance, if the view is a JSP exception or Servlet exception view
The error might be caused by searches from the value stack and returning something that you didn't expect depending on the structure of the stack at the moment of execution.
Adding a prefix to the variable to point out the exact location of the property should fix the redundancy in the code when searching in the value stack.

Mapping the enum to spring form select in a different way

I have the enum set
public enum MyEnum {
A("AND"), //
I("INTER");
}
I have the spring form:select in my jsp page which populates the AND and INTER As I want , but while submitting the form it throws the exception , can't map String AND to enum type Status.
My Controller :
model.addAttribute("list",MyEnum.values);
**
But it works when I modify my enum and add one more value like ,
OR("ÖR") , now If I select OR from UI , it gets saved properly in the
database.But it won't work for A or I.
**
Note : Down voting to this question simply means that you have not read question carefully.I have already searched on stackoverflow. :)
thanks all for answering but I found the way , how to deal with this.
<form:options items="${mList}" itemLabel="name" />
Use spring form options in following way don't specify the itemValue field , spring will do it for you. :)
I suppose when spring converts your enum to select option it uses toString method on enum and you overrided it so it returns "AND" for A and "INTER" for I. When it converts selected option back to enum it probably uses valueOf but I'm not sure.
In any case you should try to implemend your own formatter for this field that will convert enum to String and back to enum correctly. Check this article: http://bthurley.wordpress.com/2012/10/25/enum-translations-in-spring-mvc/

Access Map<Enum, Object> in JSTL

I have:
public enum MyEnum{
One, Two, Three
}
From controller, I put in the model:
HashMap<MyEnum, Long> map = new HashMap<MyEnum, Long>();
map.put(MyEnum.One, 1L);
mav.addObject( "map", map);
How do I in my JSTL access the object in the map for key enum MyEnum.One, in a neat way?
${map['One']} //does not seem to work...
nor does
${map[MyEnum.One]}
It's not exactly true that you can't do it, but the solution isn't completely straight forward. The issue is EL is not converting the string you pass in as the map key to the corresponding enum for you, so putting ${map['One']} does not use the enum constant MyEnum.One in the map lookup.
I ran into the same issue and didn't want to revert to going with a String keyed map, so the challenge then was in JSTL how to get the actual enum reference to use in the map lookup.
What is required is to get the Enum constants into the scope of the JSP so that you can then use the actual Enum itself as the key. To do this, in the controller you do something like this:
for (MyEnum e : MyEnum.values()) {
request.putAttribute(e.toString(), e);
}
What you've done here is added variables into the scope named as the string representation of the enum. (you could of course avoid naming issues by prepending the e.toSring() with some value)
Now, when you do the following
${map[ONE]}
You will be using the actual enum constant as the key and will therefore get back the proper corresponding value from the map. (notice there are no quotes around ONE, that is because you are referencing the request attribute ONE in this case, that was added above)
You can't. Your best bet is to change your map to use enum.name() as key:
HashMap<String, Long> map = new HashMap<String, Long>();
map.put(MyEnum.One.name, 1L);
map.addObject( "map", map);
Your first approach would work then:
${map['One']} // works now
Or you can write a custom EL function to do the above for you if you can't / don't want to change the map.
I usually use this solution:
<%#page import="my.package.MyEnum"%>
<c:set var="One" value="<%=MyEnum.One %>" />
<c:set var="MyEnum_values" value="${map[One]}" />
First, I import the enum. Then, I save the enum value I want into JSTL variable. Then I can access the map with this variable as the key.
${map[MyEnum.One]}
It works for me. But you have to write the complete name of your class: my.package.MyEnum or to import MyEnum class:
<%#page import="my.package.MyEnum"%>

displaytag: suppress zeros in HTML table

I have a HTML table that's generated in a JSP by the displaytag tag library. I would like to suppress any zeros that appear in the table, i.e. they should be replaced by a blank cell. Is there any straightforward way to achieve this?
I discovered that this can be achieved using a custom implementation of ColumnDecorator.
public class SuppressZeroDecorator implements DisplaytagColumnDecorator {
/* (non-Javadoc)
* #see org.displaytag.decorator.DisplaytagColumnDecorator#decorate(java.lang.Object, javax.servlet.jsp.PageContext, org.displaytag.properties.MediaTypeEnum)
*/
public Object decorate(Object rowObject, PageContext pageContext, MediaTypeEnum mediaType) {
if (rowObject != null && rowObject.toString().trim().equals("0")) {
return null;
}
return rowObject;
}
}
The decorator should be declared for each column in the JSP like this:
<display:column property="age" title="Age" decorator="com.example.ZeroColumnDecorator" />
Create a org.displaytag.decorator.TableDecorator instance, and place it into the table. Use the display:table tag's decorator attribute to place your fully qualified decorator class name into the table (I believe you can instantiate one and then place it in, but this class instantiation is trivial...make sure you have a no-arg constructor for this to work properly).
The methods initRow(...) or startRow() are where you would go through your table object, setting any zeroes you find to null (or, if displaying nulls, a blank space). I recommend initRow, but make sure you use super.initRow() first to easily access the current row object. The reason I recommend this is that startRow must return a String (defaults to returning a null string) which I don't think you need to do.
To my knowledge there isn't an "straightforward" way of handling this. The only data-related config property is whether to display nulls or not.
You're better off handling this before the data gets to to the displaytag tag. Perhaps in the servlet that provides the data or a view helper class.

Categories