NIHVIVO-3087 Improve handling of method invocation in argumentless method dump. Add unit test for this case.

This commit is contained in:
ryounes 2011-08-05 15:08:44 +00:00
parent 4229e04a02
commit 092f6ed4d8
4 changed files with 59 additions and 47 deletions

View file

@ -0,0 +1,21 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package freemarker.ext.beans;
/**
* Class to expose template model wrapper. Used as workaround to gaps
* in the Freemarker template model API (can't get wrapper for an
* arbitrary template model object).
*/
public class WrapperExtractor {
public static BeansWrapper getWrapper(BeanModel model) {
return model.wrapper;
}
public static int getWrapperExposureLevel(BeanModel model) {
return model.wrapper.getExposureLevel();
}
}

View file

@ -1,25 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package freemarker.ext.beans;
import java.lang.reflect.Member;
/**
* Class to expose protected information about template models and their data
* and wrappers to dump methods. Used as workaround to some problems and gaps
* in the Freemarker template model API.
*/
public class WrapperUtils {
public static BeansWrapper getWrapper(BeanModel model) {
return model.wrapper;
}
public static int getWrapperExposureLevel(BeanModel model) {
return model.wrapper.getExposureLevel();
}
public static Member getMember(SimpleMethodModel model) {
return model.getMember();
}
}

View file

@ -28,7 +28,8 @@ import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.CollectionModel; import freemarker.ext.beans.CollectionModel;
import freemarker.ext.beans.SimpleMethodModel; import freemarker.ext.beans.SimpleMethodModel;
import freemarker.ext.beans.StringModel; import freemarker.ext.beans.StringModel;
import freemarker.ext.beans.WrapperUtils; import freemarker.ext.beans.WrapperExtractor;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateBooleanModel; import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateCollectionModel; import freemarker.template.TemplateCollectionModel;
@ -58,7 +59,7 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel {
private static final String TEMPLATE_DEFAULT = "dump.ftl"; // change to dump.ftl when old dump is removed private static final String TEMPLATE_DEFAULT = "dump.ftl"; // change to dump.ftl when old dump is removed
private static final Pattern PROPERTY_NAME_PATTERN = Pattern.compile("^(get|is)\\w"); private static final Pattern PROPERTY_NAME_PATTERN = Pattern.compile("^(get|is)\\w");
private BeansWrapper wrapper; private ObjectWrapper defaultWrapper;
enum Key { enum Key {
CLASS("class"), CLASS("class"),
@ -139,6 +140,11 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel {
protected Map<String, Object> getTemplateVariableDump(String varName, Environment env) protected Map<String, Object> getTemplateVariableDump(String varName, Environment env)
throws TemplateModelException { throws TemplateModelException {
defaultWrapper = env.getObjectWrapper();
if (defaultWrapper == null) {
defaultWrapper = env.getConfiguration().getObjectWrapper();
}
TemplateHashModel dataModel = env.getDataModel(); TemplateHashModel dataModel = env.getDataModel();
TemplateModel model = dataModel.get(varName); TemplateModel model = dataModel.get(varName);
return getTemplateVariableDump(varName, model); return getTemplateVariableDump(varName, model);
@ -393,15 +399,11 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel {
// If no arguments, invoke the method to get the result // If no arguments, invoke the method to get the result
if ( methodDisplayName.endsWith("()") ) { if ( methodDisplayName.endsWith("()") ) {
SimpleMethodModel methodModel = (SimpleMethodModel)model.get(methodName); SimpleMethodModel methodModel = (SimpleMethodModel)model.get(methodName);
Member member = WrapperUtils.getMember(methodModel);
try { try {
if (member instanceof Method) { Object result = methodModel.exec(null);
Method m = (Method) member; ObjectWrapper wrapper = getWrapper(model);
Object result = m.invoke(object); TemplateModel wrappedResult = wrapper.wrap(result);
BeansWrapper wrapper = getWrapper(model); methods.put(methodDisplayName, getDump(wrappedResult));
TemplateModel wrappedResult = wrapper.wrap(result);
methods.put(methodDisplayName, getDump(wrappedResult));
}
} catch (Exception e) { } catch (Exception e) {
log.error(e, e); log.error(e, e);
} }
@ -426,10 +428,14 @@ public abstract class BaseDumpDirective implements TemplateDirectiveModel {
return map; return map;
} }
private BeansWrapper getWrapper(TemplateHashModelEx model) { private ObjectWrapper getWrapper(TemplateHashModelEx model) {
// Attempt to find the wrapper that this template model object was wrapped with.
if (model instanceof BeanModel) { if (model instanceof BeanModel) {
return WrapperUtils.getWrapper((BeanModel)model); return WrapperExtractor.getWrapper((BeanModel)model);
// Otherwise return the wrapper defined for the Environment or Configuration,
// if there is one. Why can't we get the wrapper for any type of TemplateModel??
} else if (defaultWrapper != null) {
return defaultWrapper;
} else { } else {
return new BeansWrapper(); return new BeansWrapper();
} }

View file

@ -1119,6 +1119,10 @@ public class DumpDirectiveTest {
return supervisor; return supervisor;
} }
public Employee boss() {
return supervisor;
}
public List<String> getFavoriteColors() { public List<String> getFavoriteColors() {
return favoriteColors; return favoriteColors;
} }
@ -1151,6 +1155,10 @@ public class DumpDirectiveTest {
private Map<String, Object> getJohnDoeExpectedDump(int exposureLevel) { private Map<String, Object> getJohnDoeExpectedDump(int exposureLevel) {
Map<String, Object> expectedDump = new HashMap<String, Object>(); Map<String, Object> expectedDump = new HashMap<String, Object>();
Map<String, Object> supervisorExpectedDump = new HashMap<String, Object>();
supervisorExpectedDump.put(Key.TYPE.toString(), "freemarker.ext.dump.DumpDirectiveTest$Employee");
supervisorExpectedDump.put(Key.VALUE.toString(), getJaneSmithExpectedDump(exposureLevel));
// Properties // Properties
SortedMap<String, Object> propertiesExpectedDump = new TreeMap<String, Object>(); SortedMap<String, Object> propertiesExpectedDump = new TreeMap<String, Object>();
@ -1190,10 +1198,7 @@ public class DumpDirectiveTest {
marriedExpectedDump.put(Key.VALUE.toString(), true); marriedExpectedDump.put(Key.VALUE.toString(), true);
propertiesExpectedDump.put("married", marriedExpectedDump); propertiesExpectedDump.put("married", marriedExpectedDump);
Map<String, Object> supervisorExpectedDump = new HashMap<String, Object>();
supervisorExpectedDump.put(Key.TYPE.toString(), "freemarker.ext.dump.DumpDirectiveTest$Employee");
supervisorExpectedDump.put(Key.VALUE.toString(), getJaneSmithExpectedDump(exposureLevel));
propertiesExpectedDump.put("supervisor", supervisorExpectedDump); propertiesExpectedDump.put("supervisor", supervisorExpectedDump);
Map<String, Object> favoriteColorsExpectedDump = new HashMap<String, Object>(); Map<String, Object> favoriteColorsExpectedDump = new HashMap<String, Object>();
@ -1213,8 +1218,10 @@ public class DumpDirectiveTest {
expectedDump.put(Key.PROPERTIES.toString(), propertiesExpectedDump); expectedDump.put(Key.PROPERTIES.toString(), propertiesExpectedDump);
// Methods // Methods
expectedDump.put(Key.METHODS.toString(), getEmployeeMethodsExpectedDump(exposureLevel, "Doe")); SortedMap<String, Object> methodDump = getEmployeeMethodsExpectedDump(exposureLevel, "Doe");
methodDump.put("boss()", supervisorExpectedDump);
expectedDump.put(Key.METHODS.toString(), methodDump);
return expectedDump; return expectedDump;
} }
@ -1250,6 +1257,9 @@ public class DumpDirectiveTest {
private Map<String, Object> getJaneSmithExpectedDump(int exposureLevel) { private Map<String, Object> getJaneSmithExpectedDump(int exposureLevel) {
Map<String, Object> expectedDump = new HashMap<String, Object>(); Map<String, Object> expectedDump = new HashMap<String, Object>();
Map<String, Object> supervisorExpectedDump = new HashMap<String, Object>();
supervisorExpectedDump.put(Key.VALUE.toString(), Value.NULL);
SortedMap<String, Object> propertiesExpectedDump = new TreeMap<String, Object>(); SortedMap<String, Object> propertiesExpectedDump = new TreeMap<String, Object>();
@ -1289,8 +1299,6 @@ public class DumpDirectiveTest {
marriedExpectedDump.put(Key.VALUE.toString(), true); marriedExpectedDump.put(Key.VALUE.toString(), true);
propertiesExpectedDump.put("married", marriedExpectedDump); propertiesExpectedDump.put("married", marriedExpectedDump);
Map<String, Object> supervisorExpectedDump = new HashMap<String, Object>();
supervisorExpectedDump.put(Key.VALUE.toString(), Value.NULL);
propertiesExpectedDump.put("supervisor", supervisorExpectedDump); propertiesExpectedDump.put("supervisor", supervisorExpectedDump);
Map<String, Object> favoriteColorsExpectedDump = new HashMap<String, Object>(); Map<String, Object> favoriteColorsExpectedDump = new HashMap<String, Object>();
@ -1310,7 +1318,9 @@ public class DumpDirectiveTest {
expectedDump.put(Key.PROPERTIES.toString(), propertiesExpectedDump); expectedDump.put(Key.PROPERTIES.toString(), propertiesExpectedDump);
// Methods // Methods
expectedDump.put(Key.METHODS.toString(), getEmployeeMethodsExpectedDump(exposureLevel, "Smith")); SortedMap<String, Object> methodDump = getEmployeeMethodsExpectedDump(exposureLevel, "Smith");
methodDump.put("boss()", supervisorExpectedDump);
expectedDump.put(Key.METHODS.toString(), methodDump);
return expectedDump; return expectedDump;
} }