Improvements to dump methods
This commit is contained in:
parent
28b6c4b867
commit
316cb523c3
4 changed files with 180 additions and 78 deletions
|
@ -71,7 +71,7 @@ public class DescribeDirective extends BaseTemplateDirectiveModel {
|
|||
}
|
||||
|
||||
DumpHelper helper = new DumpHelper(env);
|
||||
List<Method> methods = helper.getMethodsAvailableToTemplate(wrappedModel, unwrappedModel.getClass());
|
||||
List<Method> methods = helper.getMethodsAvailableToTemplate(wrappedModel, (BaseTemplateModel)unwrappedModel);
|
||||
List<String> methodDisplayNames = new ArrayList<String>(methods.size());
|
||||
for (Method m : methods) {
|
||||
methodDisplayNames.add(helper.getMethodDisplayName(m));
|
||||
|
|
|
@ -9,8 +9,10 @@ import java.lang.reflect.Modifier;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
@ -41,7 +43,7 @@ import freemarker.template.utility.DeepUnwrap;
|
|||
public class DumpHelper {
|
||||
|
||||
private static final Log log = LogFactory.getLog(DumpHelper.class);
|
||||
|
||||
|
||||
private Environment env = null;
|
||||
|
||||
public DumpHelper(Environment env) {
|
||||
|
@ -66,6 +68,10 @@ public class DumpHelper {
|
|||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("var", varName);
|
||||
|
||||
// RY Separate out here into a method that gets the rest of the map. This method will
|
||||
// be called recursively on the result of invoking a method (for a BaseTemplateModel) or
|
||||
// the members of a hash or sequence. See NewDumpHelper.getVariableDumpData().
|
||||
|
||||
// DON'T return null if wrappedModel == null. We still want to return the map to the template.
|
||||
if (wrappedModel != null) {
|
||||
Object unwrappedModel = null;
|
||||
|
@ -93,7 +99,7 @@ public class DumpHelper {
|
|||
// if (wrappedModel instanceof TemplateHashModelEx) -
|
||||
// a subcase of this is unwrappedModel instanceof BaseTemplateModel
|
||||
if (unwrappedModel instanceof BaseTemplateModel) {
|
||||
map.putAll(getTemplateModelValues(wrappedModel, (BaseTemplateModel)unwrappedModel));
|
||||
map.putAll(getBaseTemplateModelValues(wrappedModel, (BaseTemplateModel)unwrappedModel));
|
||||
type = className;
|
||||
// Do TemplateHashModel case first, because wrappedModel of (at least some) POJOs are
|
||||
// StringModels, which are both TemplateScalarModels and TemplateHashModels
|
||||
|
@ -131,39 +137,14 @@ public class DumpHelper {
|
|||
|
||||
return map;
|
||||
}
|
||||
|
||||
public void writeDump(String templateName, Map<String, Object> map, String modelName, TemplateHashModel dataModel) {
|
||||
|
||||
// Add objects to data model of calling template that are needed by
|
||||
// dump templates.
|
||||
try {
|
||||
map.put("stylesheets", dataModel.get("stylesheets"));
|
||||
map.put("urls", dataModel.get("urls"));
|
||||
} catch (TemplateModelException e) {
|
||||
log.error("Error getting values from data model.");
|
||||
}
|
||||
|
||||
String output = BaseTemplateDirectiveModel.processTemplateToString(templateName, map, env);
|
||||
Writer out = env.getOut();
|
||||
try {
|
||||
out.write(output);
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing dump of " + modelName + ".");
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Method> getMethodsAvailableToTemplate(TemplateModel wrappedModel, Class<?> cls) {
|
||||
int exposureLevel = getExposureLevel(wrappedModel, cls);
|
||||
return getMethodsAvailableToTemplate(exposureLevel, cls);
|
||||
}
|
||||
|
||||
private int getExposureLevel(TemplateModel model, Class<?> cls) {
|
||||
|
||||
private int getExposureLevel(TemplateModel model, BaseTemplateModel unwrappedModel) {
|
||||
|
||||
int exposureLevel;
|
||||
// Get the exposure level of the BeansWrapper that wrapped this object.
|
||||
if (model instanceof BeanModel) {
|
||||
exposureLevel = WrapperExtractor.getWrapperExposureLevel((BeanModel) model);
|
||||
log.debug("Exposure level for class " + cls.getCanonicalName() + " wrapped as " + model.getClass() + " = " + exposureLevel);
|
||||
log.debug("Exposure level for class " + unwrappedModel.getClass().getCanonicalName() + " wrapped as " + model.getClass() + " = " + exposureLevel);
|
||||
// We don't expect to get here, since we are dealing only with BaseTemplateModel objects, which get wrapped into BeanModel objects,
|
||||
// but it's here as a safety net.
|
||||
} else {
|
||||
|
@ -171,43 +152,51 @@ public class DumpHelper {
|
|||
Configuration config = (Configuration) request.getAttribute("freemarkerConfig");
|
||||
BeansWrapper wrapper = (BeansWrapper) config.getObjectWrapper();
|
||||
exposureLevel = WrapperExtractor.getWrapperExposureLevel(wrapper);
|
||||
log.debug("Class " + cls.getCanonicalName() + " wrapped as " + model.getClass() + " uses default exposure level " + exposureLevel);
|
||||
log.debug("Class " + unwrappedModel.getClass().getCanonicalName() + " wrapped as " + model.getClass() + " uses default exposure level " + exposureLevel);
|
||||
}
|
||||
|
||||
return exposureLevel;
|
||||
}
|
||||
|
||||
private List<Method> getMethodsAvailableToTemplate(int exposureLevel, Class<?> cls) {
|
||||
List<Method> methods = new ArrayList<Method>();
|
||||
|
||||
// Go up the class hierarchy only as far as the immediate subclass of BaseTemplateModel
|
||||
if (! cls.getName().equals("edu.cornell.mannlib.vitro.webapp.web.templatemodels.BaseTemplateModel")) {
|
||||
methods = getDeclaredMethodsAvailableToTemplate(exposureLevel, cls);
|
||||
methods.addAll(getMethodsAvailableToTemplate(exposureLevel, cls.getSuperclass()));
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
private List<Method> getDeclaredMethodsAvailableToTemplate(int exposureLevel, Class<?> cls) {
|
||||
|
||||
List<Method> methods = new ArrayList<Method>();
|
||||
Method[] declaredMethods = cls.getDeclaredMethods();
|
||||
for (Method method : declaredMethods) {
|
||||
int mod = method.getModifiers();
|
||||
if (Modifier.isPublic(mod) && !Modifier.isStatic(mod)) {
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
// If the method takes arguments, then it is not available to the template unless
|
||||
// the exposure level of the BeansWrapper that wrapped the object allows it.
|
||||
if (params.length > 0 && exposureLevel > BeansWrapper.EXPOSE_SAFE) {
|
||||
continue;
|
||||
}
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
List<Method> getMethodsAvailableToTemplate(TemplateModel wrappedModel, BaseTemplateModel unwrappedModel) {
|
||||
int exposureLevel = getExposureLevel(wrappedModel, unwrappedModel);
|
||||
return getMethodsAvailableToTemplate(exposureLevel, unwrappedModel);
|
||||
}
|
||||
|
||||
private List<Method> getMethodsAvailableToTemplate(int exposureLevel, BaseTemplateModel unwrappedModel) {
|
||||
List<Method> availableMethods = new ArrayList<Method>();
|
||||
|
||||
Class<?> cls = unwrappedModel.getClass();
|
||||
Method[] classMethods = cls.getMethods();
|
||||
for (Method method : classMethods) {
|
||||
|
||||
// Exclude static methods
|
||||
int mod = method.getModifiers();
|
||||
if (Modifier.isStatic(mod)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Include only methods declared on BaseTemplateModel or a subclass;
|
||||
// exclude methods inherited from higher up the hierarchy like
|
||||
// toString(), getClass(), etc.
|
||||
Class<?> c = method.getDeclaringClass();
|
||||
if ( ! BaseTemplateModel.class.isAssignableFrom(c)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the method takes arguments, then it is not available to the template unless
|
||||
// the exposure level of the BeansWrapper that wrapped the object allows it.
|
||||
Class<?>[] params = method.getParameterTypes();
|
||||
if (params.length > 0 && exposureLevel > BeansWrapper.EXPOSE_SAFE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
availableMethods.add(method);
|
||||
}
|
||||
|
||||
return availableMethods;
|
||||
}
|
||||
|
||||
protected String getMethodDisplayName(Method method) {
|
||||
String methodName = method.getName();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
|
@ -230,25 +219,28 @@ public class DumpHelper {
|
|||
return methodName;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelValues(TemplateModel wrappedModel, BaseTemplateModel unwrappedModel) {
|
||||
int exposureLevel = getExposureLevel(wrappedModel, unwrappedModel.getClass());
|
||||
return getTemplateModelValues(unwrappedModel, exposureLevel);
|
||||
private Map<String, Object> getBaseTemplateModelValues(TemplateModel wrappedModel, BaseTemplateModel unwrappedModel) {
|
||||
int exposureLevel = getExposureLevel(wrappedModel, unwrappedModel);
|
||||
return getBaseTemplateModelValues(unwrappedModel, exposureLevel);
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelValues(BaseTemplateModel model, int exposureLevel) {
|
||||
private Map<String, Object> getBaseTemplateModelValues(BaseTemplateModel model, int exposureLevel) {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("value", model.toString());
|
||||
|
||||
List<Method> publicMethods = getMethodsAvailableToTemplate(exposureLevel, model.getClass());
|
||||
List<Method> availableMethods = getMethodsAvailableToTemplate(exposureLevel, model);
|
||||
SortedMap<String, String> properties = new TreeMap<String, String>();
|
||||
List<String> methods = new ArrayList<String>();
|
||||
for (Method method : publicMethods) {
|
||||
for (Method method : availableMethods) {
|
||||
String key = getMethodDisplayName(method);
|
||||
if (key.endsWith(")")) {
|
||||
methods.add(key);
|
||||
} else {
|
||||
try {
|
||||
Object result = method.invoke(model);
|
||||
// RY Here we need to recurse in order to dump the full value of
|
||||
// the result, by calling a method formed from the second part
|
||||
// of getVariableDumpData.
|
||||
String value;
|
||||
if (result == null) {
|
||||
value = "null";
|
||||
|
@ -275,8 +267,28 @@ public class DumpHelper {
|
|||
}
|
||||
|
||||
private String getTemplateModelDump(BaseTemplateModel model, int exposureLevel) {
|
||||
Map<String, Object> map = getTemplateModelValues(model, exposureLevel);
|
||||
Map<String, Object> map = getBaseTemplateModelValues(model, exposureLevel);
|
||||
return BaseTemplateDirectiveModel.processTemplateToString("dump-var.ftl", map, env);
|
||||
}
|
||||
|
||||
public void writeDump(String templateName, Map<String, Object> map, String modelName, TemplateHashModel dataModel) {
|
||||
|
||||
// Add objects to data model of calling template that are needed by
|
||||
// all dump templates.
|
||||
try {
|
||||
map.put("stylesheets", dataModel.get("stylesheets"));
|
||||
map.put("urls", dataModel.get("urls"));
|
||||
} catch (TemplateModelException e) {
|
||||
log.error("Error getting values from data model.");
|
||||
}
|
||||
|
||||
String output = BaseTemplateDirectiveModel.processTemplateToString(templateName, map, env);
|
||||
Writer out = env.getOut();
|
||||
try {
|
||||
out.write(output);
|
||||
} catch (IOException e) {
|
||||
log.error("Error writing dump of " + modelName + ".");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,15 +2,11 @@
|
|||
|
||||
package edu.cornell.mannlib.vitro.webapp.web.templatemodels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap;
|
||||
|
||||
|
@ -20,8 +16,8 @@ public abstract class BaseTemplateModel {
|
|||
|
||||
protected static ServletContext servletContext = null;
|
||||
|
||||
// Wrap UrlBuilder method so templates can call ${item.url}
|
||||
public String getUrl(String path) {
|
||||
// Convenience method so subclasses can call getUrl(path)
|
||||
protected String getUrl(String path) {
|
||||
return UrlBuilder.getUrl(path);
|
||||
}
|
||||
|
||||
|
@ -43,8 +39,4 @@ public abstract class BaseTemplateModel {
|
|||
servletContext = context;
|
||||
}
|
||||
|
||||
public String dump() {
|
||||
return toString(); // fallback when subclass doesn't define a class-specific dump()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
98
webapp/web/templates/freemarker/lib/lib-dump.ftl
Normal file
98
webapp/web/templates/freemarker/lib/lib-dump.ftl
Normal file
|
@ -0,0 +1,98 @@
|
|||
<#-- dump.ftl
|
||||
--
|
||||
-- Generates tree representations of data model items.
|
||||
--
|
||||
-- Usage:
|
||||
-- <#import "dump.ftl" as dumper>
|
||||
--
|
||||
-- <#assign foo = something.in["your"].data[0].model />
|
||||
--
|
||||
-- <@dumper.dump foo />
|
||||
--
|
||||
-- When used within html pages you've to use <pre>-tags to get the wanted
|
||||
-- result:
|
||||
-- <pre>
|
||||
-- <@dumper.dump foo />
|
||||
-- <pre>
|
||||
-->
|
||||
|
||||
<#-- The black_list contains bad hash keys. Any hash key which matches a
|
||||
-- black_list entry is prevented from being displayed.
|
||||
-->
|
||||
<#assign black_list = ["class"] />
|
||||
|
||||
|
||||
<#--
|
||||
-- The main macro.
|
||||
-->
|
||||
|
||||
<#macro dump data>
|
||||
(root)
|
||||
<#if data?is_enumerable>
|
||||
<@printList data,[] />
|
||||
<#elseif data?is_hash_ex>
|
||||
<@printHashEx data,[] />
|
||||
</#if>
|
||||
</#macro>
|
||||
|
||||
<#-- private helper macros. it's not recommended to use these macros from
|
||||
-- outside the macro library.
|
||||
-->
|
||||
|
||||
<#macro printList list has_next_array>
|
||||
<#local counter=0 />
|
||||
<#list list as item>
|
||||
<#list has_next_array+[true] as has_next><#if !has_next> <#else> | </#if></#list>
|
||||
<#list has_next_array as has_next><#if !has_next> <#else> | </#if></#list><#t>
|
||||
<#t><@printItem item?if_exists,has_next_array+[item_has_next], counter />
|
||||
<#local counter = counter + 1/>
|
||||
</#list>
|
||||
</#macro>
|
||||
|
||||
<#macro printHashEx hash has_next_array>
|
||||
<#list hash?keys as key>
|
||||
<#list has_next_array+[true] as has_next><#if !has_next> <#else> | </#if></#list>
|
||||
<#list has_next_array as has_next><#if !has_next> <#else> | </#if></#list><#t>
|
||||
<#t><@printItem hash[key]?if_exists,has_next_array+[key_has_next], key />
|
||||
</#list>
|
||||
</#macro>
|
||||
|
||||
<#macro printItem item has_next_array key>
|
||||
<#if item?is_method>
|
||||
+- ${key} = ?? (method)
|
||||
<#elseif item?is_enumerable>
|
||||
+- ${key}
|
||||
<@printList item, has_next_array /><#t>
|
||||
<#elseif item?is_hash_ex && omit(key?string)><#-- omit bean-wrapped java.lang.Class objects -->
|
||||
+- ${key} (omitted)
|
||||
<#elseif item?is_hash_ex>
|
||||
+- ${key}
|
||||
<@printHashEx item, has_next_array /><#t>
|
||||
<#elseif item?is_number>
|
||||
+- ${key} = ${item}
|
||||
<#elseif item?is_string>
|
||||
+- ${key} = "${item}"
|
||||
<#elseif item?is_boolean>
|
||||
+- ${key} = ${item?string}
|
||||
<#elseif item?is_date>
|
||||
+- ${key} = ${item?string("yyyy-MM-dd HH:mm:ss zzzz")}
|
||||
<#elseif item?is_transform>
|
||||
+- ${key} = ?? (transform)
|
||||
<#elseif item?is_macro>
|
||||
+- ${key} = ?? (macro)
|
||||
<#elseif item?is_hash>
|
||||
+- ${key} = ?? (hash)
|
||||
<#elseif item?is_node>
|
||||
+- ${key} = ?? (node)
|
||||
</#if>
|
||||
</#macro>
|
||||
|
||||
<#function omit key>
|
||||
<#local what = key?lower_case>
|
||||
<#list black_list as item>
|
||||
<#if what?index_of(item) gte 0>
|
||||
<#return true>
|
||||
</#if>
|
||||
</#list>
|
||||
<#return false>
|
||||
</#function>
|
Loading…
Add table
Reference in a new issue