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);
|
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());
|
List<String> methodDisplayNames = new ArrayList<String>(methods.size());
|
||||||
for (Method m : methods) {
|
for (Method m : methods) {
|
||||||
methodDisplayNames.add(helper.getMethodDisplayName(m));
|
methodDisplayNames.add(helper.getMethodDisplayName(m));
|
||||||
|
|
|
@ -9,8 +9,10 @@ import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.SortedMap;
|
import java.util.SortedMap;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
@ -66,6 +68,10 @@ public class DumpHelper {
|
||||||
Map<String, Object> map = new HashMap<String, Object>();
|
Map<String, Object> map = new HashMap<String, Object>();
|
||||||
map.put("var", varName);
|
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.
|
// DON'T return null if wrappedModel == null. We still want to return the map to the template.
|
||||||
if (wrappedModel != null) {
|
if (wrappedModel != null) {
|
||||||
Object unwrappedModel = null;
|
Object unwrappedModel = null;
|
||||||
|
@ -93,7 +99,7 @@ public class DumpHelper {
|
||||||
// if (wrappedModel instanceof TemplateHashModelEx) -
|
// if (wrappedModel instanceof TemplateHashModelEx) -
|
||||||
// a subcase of this is unwrappedModel instanceof BaseTemplateModel
|
// a subcase of this is unwrappedModel instanceof BaseTemplateModel
|
||||||
if (unwrappedModel instanceof BaseTemplateModel) {
|
if (unwrappedModel instanceof BaseTemplateModel) {
|
||||||
map.putAll(getTemplateModelValues(wrappedModel, (BaseTemplateModel)unwrappedModel));
|
map.putAll(getBaseTemplateModelValues(wrappedModel, (BaseTemplateModel)unwrappedModel));
|
||||||
type = className;
|
type = className;
|
||||||
// Do TemplateHashModel case first, because wrappedModel of (at least some) POJOs are
|
// Do TemplateHashModel case first, because wrappedModel of (at least some) POJOs are
|
||||||
// StringModels, which are both TemplateScalarModels and TemplateHashModels
|
// StringModels, which are both TemplateScalarModels and TemplateHashModels
|
||||||
|
@ -132,38 +138,13 @@ public class DumpHelper {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeDump(String templateName, Map<String, Object> map, String modelName, TemplateHashModel dataModel) {
|
private int getExposureLevel(TemplateModel model, BaseTemplateModel unwrappedModel) {
|
||||||
|
|
||||||
// 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) {
|
|
||||||
|
|
||||||
int exposureLevel;
|
int exposureLevel;
|
||||||
// Get the exposure level of the BeansWrapper that wrapped this object.
|
// Get the exposure level of the BeansWrapper that wrapped this object.
|
||||||
if (model instanceof BeanModel) {
|
if (model instanceof BeanModel) {
|
||||||
exposureLevel = WrapperExtractor.getWrapperExposureLevel((BeanModel) model);
|
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,
|
// 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.
|
// but it's here as a safety net.
|
||||||
} else {
|
} else {
|
||||||
|
@ -171,41 +152,49 @@ public class DumpHelper {
|
||||||
Configuration config = (Configuration) request.getAttribute("freemarkerConfig");
|
Configuration config = (Configuration) request.getAttribute("freemarkerConfig");
|
||||||
BeansWrapper wrapper = (BeansWrapper) config.getObjectWrapper();
|
BeansWrapper wrapper = (BeansWrapper) config.getObjectWrapper();
|
||||||
exposureLevel = WrapperExtractor.getWrapperExposureLevel(wrapper);
|
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;
|
return exposureLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Method> getMethodsAvailableToTemplate(int exposureLevel, Class<?> cls) {
|
List<Method> getMethodsAvailableToTemplate(TemplateModel wrappedModel, BaseTemplateModel unwrappedModel) {
|
||||||
List<Method> methods = new ArrayList<Method>();
|
int exposureLevel = getExposureLevel(wrappedModel, unwrappedModel);
|
||||||
|
return getMethodsAvailableToTemplate(exposureLevel, unwrappedModel);
|
||||||
// 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) {
|
private List<Method> getMethodsAvailableToTemplate(int exposureLevel, BaseTemplateModel unwrappedModel) {
|
||||||
|
List<Method> availableMethods = new ArrayList<Method>();
|
||||||
|
|
||||||
List<Method> methods = new ArrayList<Method>();
|
Class<?> cls = unwrappedModel.getClass();
|
||||||
Method[] declaredMethods = cls.getDeclaredMethods();
|
Method[] classMethods = cls.getMethods();
|
||||||
for (Method method : declaredMethods) {
|
for (Method method : classMethods) {
|
||||||
|
|
||||||
|
// Exclude static methods
|
||||||
int mod = method.getModifiers();
|
int mod = method.getModifiers();
|
||||||
if (Modifier.isPublic(mod) && !Modifier.isStatic(mod)) {
|
if (Modifier.isStatic(mod)) {
|
||||||
Class<?>[] params = method.getParameterTypes();
|
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.
|
|
||||||
if (params.length > 0 && exposureLevel > BeansWrapper.EXPOSE_SAFE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
methods.add(method);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 methods;
|
|
||||||
|
return availableMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getMethodDisplayName(Method method) {
|
protected String getMethodDisplayName(Method method) {
|
||||||
|
@ -230,25 +219,28 @@ public class DumpHelper {
|
||||||
return methodName;
|
return methodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> getTemplateModelValues(TemplateModel wrappedModel, BaseTemplateModel unwrappedModel) {
|
private Map<String, Object> getBaseTemplateModelValues(TemplateModel wrappedModel, BaseTemplateModel unwrappedModel) {
|
||||||
int exposureLevel = getExposureLevel(wrappedModel, unwrappedModel.getClass());
|
int exposureLevel = getExposureLevel(wrappedModel, unwrappedModel);
|
||||||
return getTemplateModelValues(unwrappedModel, exposureLevel);
|
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<String, Object> map = new HashMap<String, Object>();
|
||||||
map.put("value", model.toString());
|
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>();
|
SortedMap<String, String> properties = new TreeMap<String, String>();
|
||||||
List<String> methods = new ArrayList<String>();
|
List<String> methods = new ArrayList<String>();
|
||||||
for (Method method : publicMethods) {
|
for (Method method : availableMethods) {
|
||||||
String key = getMethodDisplayName(method);
|
String key = getMethodDisplayName(method);
|
||||||
if (key.endsWith(")")) {
|
if (key.endsWith(")")) {
|
||||||
methods.add(key);
|
methods.add(key);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
Object result = method.invoke(model);
|
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;
|
String value;
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
value = "null";
|
value = "null";
|
||||||
|
@ -275,8 +267,28 @@ public class DumpHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTemplateModelDump(BaseTemplateModel model, int exposureLevel) {
|
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);
|
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;
|
package edu.cornell.mannlib.vitro.webapp.web.templatemodels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
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;
|
||||||
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap;
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMap;
|
||||||
|
|
||||||
|
@ -20,8 +16,8 @@ public abstract class BaseTemplateModel {
|
||||||
|
|
||||||
protected static ServletContext servletContext = null;
|
protected static ServletContext servletContext = null;
|
||||||
|
|
||||||
// Wrap UrlBuilder method so templates can call ${item.url}
|
// Convenience method so subclasses can call getUrl(path)
|
||||||
public String getUrl(String path) {
|
protected String getUrl(String path) {
|
||||||
return UrlBuilder.getUrl(path);
|
return UrlBuilder.getUrl(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +39,4 @@ public abstract class BaseTemplateModel {
|
||||||
servletContext = context;
|
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
Add a link
Reference in a new issue