Maybe I'm being dense... I'm having some trouble setting up what yatesco suggests. I don't have Cogs, but I do have SelectableMarkers. Anyhow - I have an entity class - a QcRequest. A QcRequest has a list of child SelectableMarker's. I'm using FreeMarker (love it, btw - I hope to never touch a JSP again, ever) and have my wonderful little property editor. Seems, though, that the property editor is converting the list to a string (as in editor.getAsText()) and is driving the checkbox macro nuts. Here's the relavent code and such:
Code:
public class QcRequest {
private int pk;
...
private List<SelectableMarker> bacterialSelectableMarkers;
... various getters/setters and few methods that 'do stuff'
}
public class SelectableMarker {
private int pk;
private String marker;
private String abbreviation;
... again with the getter/setters
}
And a property editor:
Code:
public class SelectableMarkerListEditor extends PropertyEditorSupport {
private Map<String, SelectableMarker> selectableMarkerMap;
public SelectableMarkerListEditor(List<SelectableMarker> markerList) {
this.selectableMarkerMap = new HashMap<String, SelectableMarker>(markerList.size());
for (SelectableMarker marker : markerList) {
this.selectableMarkerMap.put(Integer.toString(marker.getPk()), marker);
}
}
public void setAsText(String text) {
String[] keys = text.split(",");
List<SelectableMarker> markers = new ArrayList<SelectableMarker>(keys.length);
for (int i=0; i<keys.length; ++i) {
markers.add(selectableMarkerMap.get(keys[i].trim()));
}
setValue(markers);
}
}
The relevant bit of the freemarker template that renders the control (er, blows up spectacularly - more on that below):
Code:
<tr>
<td><@genie.label "form.label.selectableMarkers"/></td>
<td><@spring.formCheckboxes path="request.bacterialSelectableMarkers"
options=selectableMarkers
separator="<br/>"/>
<@spring.showErrors separator="<br/>" classOrStyle="error"/>
</td>
</tr>
The code that populates the view model with the list o' available markers:
Code:
... from the relevant referenceData() override. (Spring MVC - good times)
Map<String, Object> model = new HashMap<String, Object>();
model.put("selectableMarkers", viewListService.getSelectableMarkerMap());
return model;
... the interface for the viewListService:
public interface ViewListService {
public Map<String, String> getVectorTypeMap();
public Map<String, String> getSelectableMarkerMap();
}
and, of course, registering the property editor:
Code:
@Override protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(List.class, "bacterialSelectableMarkers", new SelectableMarkerListEditor(selectableMarkerService.getMarkers()));
}
Ok, so that all looks fine to me. Problem is, it blows up. Not so good. Here's the lovely stack trace:
Code:
freemarker.template.TemplateException: Expected collection or sequence. list evaluated instead to freemarker.template.SimpleScalar on line 300, column 16 in spring.ftl.
freemarker.core.TemplateObject.invalidTypeException(TemplateObject.java:135)
freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:183)
freemarker.core.Environment.visit(Environment.java:351)
freemarker.core.IteratorBlock.accept(IteratorBlock.java:95)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.MixedContent.accept(MixedContent.java:92)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.Macro$Context.runMacro(Macro.java:164)
freemarker.core.Environment.visit(Environment.java:537)
freemarker.core.MethodCall._getAsTemplateModel(MethodCall.java:105)
freemarker.core.Expression.getAsTemplateModel(Expression.java:89)
freemarker.core.Assignment.accept(Assignment.java:91)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.MixedContent.accept(MixedContent.java:92)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:160)
freemarker.core.Environment.visit(Environment.java:351)
freemarker.core.IteratorBlock.accept(IteratorBlock.java:95)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.MixedContent.accept(MixedContent.java:92)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.Macro$Context.runMacro(Macro.java:164)
freemarker.core.Environment.visit(Environment.java:537)
freemarker.core.UnifiedCall.accept(UnifiedCall.java:128)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.MixedContent.accept(MixedContent.java:92)
freemarker.core.Environment.visit(Environment.java:196)
freemarker.core.Environment.process(Environment.java:176)
freemarker.template.Template.process(Template.java:231)
org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:267)
org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:221)
org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:180)
org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:160)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:928)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:705)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:625)
org.springframework.web.servlet.FrameworkServlet.serviceWrapper(FrameworkServlet.java:386)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:355)
javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
net.sf.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:292)
net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:84)
net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter.doFilter(SecurityEnforcementFilter.java:182)
net.sf.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
net.sf.acegisecurity.providers.anonymous.AnonymousProcessingFilter.doFilter(AnonymousProcessingFilter.java:154)
net.sf.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
net.sf.acegisecurity.ui.AbstractProcessingFilter.doFilter(AbstractProcessingFilter.java:311)
net.sf.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter.doFilter(HttpSessionContextIntegrationFilter.java:215)
net.sf.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
net.sf.acegisecurity.securechannel.ChannelProcessingFilter.doFilter(ChannelProcessingFilter.java:168)
net.sf.acegisecurity.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:303)
net.sf.acegisecurity.util.FilterChainProxy.doFilter(FilterChainProxy.java:173)
net.sf.acegisecurity.util.FilterToBeanProxy.doFilter(FilterToBeanProxy.java:125)
which just says that this line here is blowing up: (if you don't know freemarker macro syntax, don't worry -- it's all pretty easy)
Code:
<#function contains list item>
<#list list as nextInList> <--- line 300
<#if nextInList == item><#return true></#if>
</#list>
<#return false>
</#function>
which is being called by this:
Code:
<#macro formCheckboxes path options separator attributes="">
<@bind path/>
<#list options?keys as value>
<#assign isSelected = contains(spring.status.value?default([""]), value)> <---- which is being called here.
<input type="checkbox" name="${spring.status.expression}" value="${value}" ---- 'spring.status.value?default([""])' is a scalar?!
<#if isSelected>checked="checked"</#if> ${attributes}
<@closeTag/>
${options[value]}${separator}
</#list>
</#macro>
So, basically spring.status.value (ie, the current binding's value) is coming up as a scalar instead of as a sequence or list. In fact, if I comment out the call to the checkbox macro, call bind directly and print out the value of ${spring.status.value} - I get a lovely string-representation of my list. Which makes me think that the property editor is being applied to the property of the bean (bacterialSelectableMarkers) and the string value that the property editor is returning is being used by spring.status.value instead of the list value of the property. Which makes the macro bomb when it tries to apply a list function to it.
Am I crazy, or did I forget to do something?
Any help would be greatly appreciated......