Author: musachy Date: Fri May 18 18:25:31 2007 New Revision: 539660 URL: http://svn.apache.org/viewvc?view=rev&rev=539660 Log: WW-1830 Support non-String attributes to freemarker JSP tag extensions * port from fix on WebWork rev 2866
Added: struts/struts2/branches/STRUTS_2_0_X/core/src/test/java/org/apache/struts2/views/freemarker/tags/ struts/struts2/branches/STRUTS_2_0_X/core/src/test/java/org/apache/struts2/views/freemarker/tags/TagModelTest.java Modified: struts/struts2/branches/STRUTS_2_0_X/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java Modified: struts/struts2/branches/STRUTS_2_0_X/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_2_0_X/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java?view=diff&rev=539660&r1=539659&r2=539660 ============================================================================== --- struts/struts2/branches/STRUTS_2_0_X/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java (original) +++ struts/struts2/branches/STRUTS_2_0_X/core/src/main/java/org/apache/struts2/views/freemarker/tags/TagModel.java Fri May 18 18:25:31 2007 @@ -31,15 +31,16 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.struts2.components.ActionComponent; import org.apache.struts2.components.Component; import org.apache.struts2.dispatcher.Dispatcher; import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.util.ValueStack; +import freemarker.template.DefaultObjectWrapper; import freemarker.template.SimpleNumber; import freemarker.template.SimpleSequence; +import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; import freemarker.template.TemplateTransformModel; @@ -56,17 +57,47 @@ this.res = res; } - public Writer getWriter(Writer writer, Map params) throws TemplateModelException, IOException { + public Writer getWriter(Writer writer, Map params) + throws TemplateModelException, IOException { Component bean = getBean(); Container container = Dispatcher.getInstance().getConfigurationManager().getConfiguration().getContainer(); container.inject(bean); - Map basicParams = convertParams(params); - bean.copyParams(basicParams); - bean.addAllParameters(getComplexParams(params)); + + Map unwrappedParameters = unwrapParameters(params); + bean.copyParams(unwrappedParameters); + return new CallbackWriter(bean, writer); } protected abstract Component getBean(); + + protected Map unwrapParameters(Map params) { + Map map = new HashMap(params.size()); + DefaultObjectWrapper objectWrapper = new DefaultObjectWrapper(); + for (Iterator iterator = params.entrySet().iterator(); iterator.hasNext();) { + Map.Entry entry = (Map.Entry) iterator.next(); + + Object value = entry.getValue(); + + if (value != null) { + // the value should ALWAYS be a decendant of TemplateModel + if (value instanceof TemplateModel) { + try { + map.put(entry.getKey(), objectWrapper + .unwrap((TemplateModel) value)); + } catch (TemplateModelException e) { + LOG.error("failed to unwrap [" + value + + "] it will be ignored", e); + } + } + // if it doesn't, we'll do it the old way by just returning the toString() representation + else { + map.put(entry.getKey(), value.toString()); + } + } + } + return map; + } protected Map convertParams(Map params) { HashMap map = new HashMap(params.size()); Added: struts/struts2/branches/STRUTS_2_0_X/core/src/test/java/org/apache/struts2/views/freemarker/tags/TagModelTest.java URL: http://svn.apache.org/viewvc/struts/struts2/branches/STRUTS_2_0_X/core/src/test/java/org/apache/struts2/views/freemarker/tags/TagModelTest.java?view=auto&rev=539660 ============================================================================== --- struts/struts2/branches/STRUTS_2_0_X/core/src/test/java/org/apache/struts2/views/freemarker/tags/TagModelTest.java (added) +++ struts/struts2/branches/STRUTS_2_0_X/core/src/test/java/org/apache/struts2/views/freemarker/tags/TagModelTest.java Fri May 18 18:25:31 2007 @@ -0,0 +1,426 @@ +/* + * $Id$ + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.views.freemarker.tags; + +import java.io.StringWriter; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.struts2.StrutsTestCase; +import org.apache.struts2.components.Component; +import org.apache.struts2.views.freemarker.tags.TagModel; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import com.opensymphony.xwork2.util.OgnlValueStack; + +import freemarker.ext.util.WrapperTemplateModel; +import freemarker.template.AdapterTemplateModel; +import freemarker.template.TemplateBooleanModel; +import freemarker.template.TemplateCollectionModel; +import freemarker.template.TemplateHashModel; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; +import freemarker.template.TemplateModelIterator; +import freemarker.template.TemplateNumberModel; +import freemarker.template.TemplateScalarModel; +import freemarker.template.TemplateSequenceModel; + +public class TagModelTest extends StrutsTestCase { + + final Object ADAPTER_TEMPLATE_MODEL_CONTAINED_OBJECT = new Object(); + final Object WRAPPING_TEMPLATE_MODEL_CONTAINED_OBJECT = new Object(); + + public void testUnwrapMap() throws Exception { + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + OgnlValueStack stack = new OgnlValueStack(); + + Map params = new LinkedHashMap(); + + // Try to test out the commons Freemarker's Template Model + + // TemplateBooleanModel + params.put("property1", new TemplateBooleanModel() { + public boolean getAsBoolean() throws TemplateModelException { + return true; + } + }); + params.put("property2", new TemplateBooleanModel() { + public boolean getAsBoolean() throws TemplateModelException { + return false; + } + }); + + // TemplateScalarModel + params.put("property3", new TemplateScalarModel() { + public String getAsString() throws TemplateModelException { + return "toby"; + } + }); + params.put("property4", new TemplateScalarModel() { + public String getAsString() throws TemplateModelException { + return "phil"; + } + }); + + // TemplateNumberModel + params.put("property5", new TemplateNumberModel() { + public Number getAsNumber() throws TemplateModelException { + return new Integer("10"); + } + }); + params.put("property6", new TemplateNumberModel() { + public Number getAsNumber() throws TemplateModelException { + return new Float("1.1"); + } + }); + + // TemplateHashModel + params.put("property7", new TemplateHashModel() { + public TemplateModel get(String arg0) throws TemplateModelException { + return null; + } + + public boolean isEmpty() throws TemplateModelException { + return true; + } + }); + + // TemplateSequenceModel + params.put("property8", new TemplateSequenceModel() { + public TemplateModel get(int arg0) throws TemplateModelException { + return null; + } + + public int size() throws TemplateModelException { + return 0; + } + }); + + // TemplateCollectionModel + params.put("property9", new TemplateCollectionModel() { + public TemplateModelIterator iterator() throws TemplateModelException { + return new TemplateModelIterator() { + private Iterator i; + { + i = Collections.EMPTY_LIST.iterator(); + } + + public boolean hasNext() throws TemplateModelException { + return i.hasNext(); + } + + public TemplateModel next() throws TemplateModelException { + return (TemplateModel) i.next(); + } + }; + } + }); + + // AdapterTemplateModel + params.put("property10", new AdapterTemplateModel() { + public Object getAdaptedObject(Class arg0) { + return ADAPTER_TEMPLATE_MODEL_CONTAINED_OBJECT; + } + }); + + // WrapperTemplateModel + params.put("property11", new WrapperTemplateModel() { + public Object getWrappedObject() { + return WRAPPING_TEMPLATE_MODEL_CONTAINED_OBJECT; + } + }); + + TagModel tagModel = new TagModel(stack, request, response) { + protected Component getBean() { + return null; + } + }; + + Map result = tagModel.unwrapParameters(params); + + assertNotNull(result); + assertEquals(result.size(), 11); + assertEquals(result.get("property1"), Boolean.TRUE); + assertEquals(result.get("property2"), Boolean.FALSE); + assertEquals(result.get("property3"), "toby"); + assertEquals(result.get("property4"), "phil"); + assertEquals(result.get("property5"), new Integer(10)); + assertEquals(result.get("property6"), new Float(1.1)); + assertNotNull(result.get("property7")); + assertTrue(result.get("property7") instanceof Map); + assertNotNull(result.get("property8")); + assertTrue(result.get("property8") instanceof Collection); + assertNotNull(result.get("property9")); + assertTrue(result.get("property9") instanceof Collection); + assertEquals(result.get("property10"), + ADAPTER_TEMPLATE_MODEL_CONTAINED_OBJECT); + assertEquals(result.get("property11"), + WRAPPING_TEMPLATE_MODEL_CONTAINED_OBJECT); + } + + public void testGetWriter() throws Exception { + + OgnlValueStack stack = new OgnlValueStack(); + + final InternalBean bean = new InternalBean(stack); + + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + Map params = new LinkedHashMap(); + + // Try to test out the commons Freemarker's Template Model + + // TemplateBooleanModel + params.put("property1", new TemplateBooleanModel() { + public boolean getAsBoolean() throws TemplateModelException { + return true; + } + }); + params.put("property2", new TemplateBooleanModel() { + public boolean getAsBoolean() throws TemplateModelException { + return false; + } + }); + + // TemplateScalarModel + params.put("property3", new TemplateScalarModel() { + public String getAsString() throws TemplateModelException { + return "toby"; + } + }); + params.put("property4", new TemplateScalarModel() { + public String getAsString() throws TemplateModelException { + return "phil"; + } + }); + + // TemplateNumberModel + params.put("property5", new TemplateNumberModel() { + public Number getAsNumber() throws TemplateModelException { + return new Integer("10"); + } + }); + params.put("property6", new TemplateNumberModel() { + public Number getAsNumber() throws TemplateModelException { + return new Float("1.1"); + } + }); + + // TemplateHashModel + params.put("property7", new TemplateHashModel() { + public TemplateModel get(String arg0) throws TemplateModelException { + return null; + } + + public boolean isEmpty() throws TemplateModelException { + return true; + } + }); + + // TemplateSequenceModel + params.put("property8", new TemplateSequenceModel() { + public TemplateModel get(int arg0) throws TemplateModelException { + return null; + } + + public int size() throws TemplateModelException { + return 0; + } + }); + + // TemplateCollectionModel + params.put("property9", new TemplateCollectionModel() { + public TemplateModelIterator iterator() throws TemplateModelException { + return new TemplateModelIterator() { + private Iterator i; + { + i = Collections.EMPTY_LIST.iterator(); + } + + public boolean hasNext() throws TemplateModelException { + return i.hasNext(); + } + + public TemplateModel next() throws TemplateModelException { + return (TemplateModel) i.next(); + } + }; + } + }); + + // AdapterTemplateModel + params.put("property10", new AdapterTemplateModel() { + public Object getAdaptedObject(Class arg0) { + return ADAPTER_TEMPLATE_MODEL_CONTAINED_OBJECT; + } + }); + + // WrapperTemplateModel + params.put("property11", new WrapperTemplateModel() { + public Object getWrappedObject() { + return WRAPPING_TEMPLATE_MODEL_CONTAINED_OBJECT; + } + }); + + TagModel tagModel = new TagModel(stack, request, response) { + protected Component getBean() { + return bean; + } + }; + + tagModel.getWriter(new StringWriter(), params); + + assertNotNull(bean); + assertEquals(bean.getProperty1(), true); + assertEquals(bean.getProperty2(), false); + assertEquals(bean.getProperty3(), "toby"); + assertEquals(bean.getProperty4(), "phil"); + assertEquals(bean.getProperty5(), new Integer(10)); + assertEquals(bean.getProperty6(), new Float(1.1)); + assertNotNull(bean.getProperty7()); + assertTrue(bean.getProperty7() instanceof Map); + assertNotNull(bean.getProperty8()); + assertTrue(bean.getProperty8() instanceof Collection); + assertNotNull(bean.getProperty9()); + assertTrue(bean.getProperty9() instanceof Collection); + assertEquals(bean.getProperty10(), ADAPTER_TEMPLATE_MODEL_CONTAINED_OBJECT); + assertEquals(bean.getProperty11(), WRAPPING_TEMPLATE_MODEL_CONTAINED_OBJECT); + } + + public static class InternalBean extends Component { + + public InternalBean(OgnlValueStack stack) { + super(stack); + } + + private boolean property1 = false; // inverse of the expected result, so we could test that it works + private boolean property2 = true; // inverse of the expected result, so we could test that it works + + private String property3; + private String property4; + + private Integer property5; + private Float property6; + + private Map property7; + private Collection property8; + private Collection property9; + + private Object property10; + private Object property11; + + public void setProperty1(boolean property1) { + this.property1 = property1; + } + + public boolean getProperty1() { + return property1; + } + + public void setProperty2(boolean property2) { + this.property2 = property2; + } + + public boolean getProperty2() { + return property2; + } + + public void setProperty3(String property3) { + this.property3 = property3; + } + + public String getProperty3() { + return this.property3; + } + + public void setProperty4(String property4) { + this.property4 = property4; + } + + public String getProperty4() { + return this.property4; + } + + public void setProperty5(Integer property5) { + this.property5 = property5; + } + + public Integer getProperty5() { + return this.property5; + } + + public void setProperty6(Float property6) { + this.property6 = property6; + } + + public Float getProperty6() { + return this.property6; + } + + public void setProperty7(Map property7) { + this.property7 = property7; + } + + public Map getProperty7() { + return property7; + } + + public void setProperty8(Collection property8) { + this.property8 = property8; + } + + public Collection getProperty8() { + return property8; + } + + public void setProperty9(Collection property9) { + this.property9 = property9; + } + + public Collection getProperty9() { + return this.property9; + } + + public void setProperty10(Object property10) { + this.property10 = property10; + } + + public Object getProperty10() { + return this.property10; + } + + public void setProperty11(Object property11) { + this.property11 = property11; + } + + public Object getProperty11() { + return this.property11; + } + + } +}