Maven migration (first draft)
This commit is contained in:
parent
5e0329908c
commit
e1ff94ccaf
2866 changed files with 1112 additions and 616 deletions
22
api/src/main/java/freemarker/ext/beans/WrapperExtractor.java
Normal file
22
api/src/main/java/freemarker/ext/beans/WrapperExtractor.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package freemarker.ext.beans;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Class to extract wrapper used to wrap an object into a template model object.
|
||||
* Used as workaround to gap in 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();
|
||||
}
|
||||
|
||||
}
|
653
api/src/main/java/freemarker/ext/dump/BaseDumpDirective.java
Normal file
653
api/src/main/java/freemarker/ext/dump/BaseDumpDirective.java
Normal file
|
@ -0,0 +1,653 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package freemarker.ext.dump;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
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;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.ext.beans.BeanModel;
|
||||
import freemarker.ext.beans.BeansWrapper;
|
||||
import freemarker.ext.beans.CollectionModel;
|
||||
import freemarker.ext.beans.SimpleMethodModel;
|
||||
import freemarker.ext.beans.StringModel;
|
||||
import freemarker.ext.beans.WrapperExtractor;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.ObjectWrapper;
|
||||
import freemarker.template.SimpleScalar;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateBooleanModel;
|
||||
import freemarker.template.TemplateCollectionModel;
|
||||
import freemarker.template.TemplateDateModel;
|
||||
import freemarker.template.TemplateDirectiveModel;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.TemplateHashModel;
|
||||
import freemarker.template.TemplateHashModelEx;
|
||||
import freemarker.template.TemplateMethodModel;
|
||||
import freemarker.template.TemplateModel;
|
||||
import freemarker.template.TemplateModelException;
|
||||
import freemarker.template.TemplateModelIterator;
|
||||
import freemarker.template.TemplateNumberModel;
|
||||
import freemarker.template.TemplateScalarModel;
|
||||
import freemarker.template.TemplateSequenceModel;
|
||||
import freemarker.template.utility.DeepUnwrap;
|
||||
|
||||
/* TODO
|
||||
* - Check error messages generated for TemplateModelException-s. If too generic, need to catch, create specific
|
||||
* error message, and rethrow.
|
||||
*/
|
||||
|
||||
public abstract class BaseDumpDirective implements TemplateDirectiveModel {
|
||||
|
||||
private static final Log log = LogFactory.getLog(BaseDumpDirective.class);
|
||||
|
||||
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 ObjectWrapper defaultWrapper;
|
||||
|
||||
enum Key {
|
||||
CLASS("class"),
|
||||
DATE_TYPE("dateType"),
|
||||
HELP("help"),
|
||||
METHODS("methods"),
|
||||
PROPERTIES("properties"),
|
||||
TYPE("type"),
|
||||
VALUE("value");
|
||||
|
||||
private final String key;
|
||||
|
||||
Key(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
enum Value {
|
||||
NULL("[null]"),
|
||||
UNDEFINED("[undefined]");
|
||||
|
||||
private final String value;
|
||||
|
||||
Value(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
enum Type {
|
||||
BOOLEAN("Boolean"),
|
||||
COLLECTION("Collection"),
|
||||
DATE("Date"),
|
||||
DIRECTIVE("Directive"),
|
||||
HASH("Hash"),
|
||||
// Technically it's a HashEx, but for the templates call it a Hash
|
||||
HASH_EX("Hash"), // ("HashEx")
|
||||
METHOD("Method"),
|
||||
NUMBER("Number"),
|
||||
SEQUENCE("Sequence"),
|
||||
STRING("String");
|
||||
|
||||
private final String type;
|
||||
|
||||
Type(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
enum DateType {
|
||||
DATE("Date"),
|
||||
DATETIME("DateTime"),
|
||||
TIME("Time"),
|
||||
UNKNOWN("Unknown");
|
||||
|
||||
private final String type;
|
||||
|
||||
DateType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> getTemplateVariableDump(String varName, Environment env)
|
||||
throws TemplateModelException {
|
||||
|
||||
defaultWrapper = env.getObjectWrapper();
|
||||
if (defaultWrapper == null) {
|
||||
defaultWrapper = env.getConfiguration().getObjectWrapper();
|
||||
}
|
||||
|
||||
TemplateHashModel dataModel = env.getDataModel();
|
||||
TemplateModel valueToDump = dataModel.get(varName);
|
||||
return getTemplateVariableDump(varName, valueToDump);
|
||||
}
|
||||
|
||||
protected Map<String, Object> getTemplateVariableDump(String varName, TemplateModel valueToDump)
|
||||
throws TemplateModelException {
|
||||
|
||||
Map<String, Object> value = new HashMap<String, Object>();
|
||||
|
||||
if (valueToDump == null) {
|
||||
value.put(Key.VALUE.toString(), Value.UNDEFINED.toString());
|
||||
|
||||
// TemplateMethodModel and TemplateDirectiveModel objects can only be
|
||||
// included in the data model at the top level.
|
||||
} else if (valueToDump instanceof TemplateMethodModel) {
|
||||
value.putAll( getTemplateModelDump( ( TemplateMethodModel)valueToDump, varName ) );
|
||||
|
||||
} else if (valueToDump instanceof TemplateDirectiveModel) {
|
||||
value.putAll( getTemplateModelDump( ( TemplateDirectiveModel)valueToDump, varName ) );
|
||||
|
||||
} else {
|
||||
value.putAll(getDump(valueToDump));
|
||||
}
|
||||
|
||||
Map<String, Object> dump = new HashMap<String, Object>();
|
||||
dump.put(varName, value);
|
||||
return dump;
|
||||
}
|
||||
|
||||
private Map<String, Object> getDump(TemplateModel valueToDump) throws TemplateModelException {
|
||||
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
// Don't return null if model == null. We still want to send the map to the template.
|
||||
if (valueToDump != null) {
|
||||
|
||||
if ( valueToDump instanceof TemplateSequenceModel ) {
|
||||
if (valueToDump instanceof CollectionModel && ! ((CollectionModel)valueToDump).getSupportsIndexedAccess()) {
|
||||
map.putAll( getTemplateModelDump( (TemplateCollectionModel)valueToDump ) );
|
||||
} else {
|
||||
map.putAll( getTemplateModelDump( (TemplateSequenceModel)valueToDump ) );
|
||||
}
|
||||
|
||||
} else if ( valueToDump instanceof TemplateNumberModel ) {
|
||||
map.putAll( getTemplateModelDump( (TemplateNumberModel)valueToDump ) );
|
||||
|
||||
} else if ( valueToDump instanceof TemplateBooleanModel ) {
|
||||
map.putAll( getTemplateModelDump( (TemplateBooleanModel)valueToDump ) );
|
||||
|
||||
} else if ( valueToDump instanceof TemplateDateModel ) {
|
||||
map.putAll( getTemplateModelDump( (TemplateDateModel)valueToDump ) );
|
||||
|
||||
} else if ( valueToDump instanceof TemplateCollectionModel ) {
|
||||
map.putAll( getTemplateModelDump( (TemplateCollectionModel)valueToDump ) );
|
||||
|
||||
} else if ( valueToDump instanceof StringModel ) {
|
||||
// A StringModel can wrap either a String or a plain Java object.
|
||||
// Unwrap it to figure out what to do.
|
||||
Object unwrappedModel = DeepUnwrap.permissiveUnwrap(valueToDump);
|
||||
|
||||
if (unwrappedModel instanceof String) {
|
||||
map.putAll( getTemplateModelDump( (TemplateScalarModel)valueToDump ) );
|
||||
} else {
|
||||
map.putAll( getTemplateModelDump( (TemplateHashModelEx)valueToDump ) );
|
||||
}
|
||||
|
||||
} else if ( valueToDump instanceof TemplateScalarModel ) {
|
||||
map.putAll( getTemplateModelDump( (TemplateScalarModel)valueToDump ) );
|
||||
|
||||
} else if ( valueToDump instanceof TemplateHashModelEx ) {
|
||||
map.putAll( getTemplateModelDump( (TemplateHashModelEx)valueToDump ) );
|
||||
|
||||
} else if (valueToDump instanceof TemplateHashModel ) {
|
||||
map.putAll( getTemplateModelDump( (TemplateHashModel)valueToDump ) );
|
||||
|
||||
// Nodes and transforms not included here
|
||||
|
||||
} else {
|
||||
// We shouldn't get here; provide as a safety net.
|
||||
map.putAll( getTemplateModelDump( (TemplateModel)valueToDump ) );
|
||||
}
|
||||
} else {
|
||||
map.put(Key.VALUE.toString(), Value.NULL.toString());
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateScalarModel model) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.STRING);
|
||||
map.put(Key.VALUE.toString(), model.getAsString());
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateBooleanModel model) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.BOOLEAN);
|
||||
map.put(Key.VALUE.toString(), model.getAsBoolean());
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateNumberModel model) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.NUMBER);
|
||||
map.put(Key.VALUE.toString(), model.getAsNumber());
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateDateModel model) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.DATE);
|
||||
int dateType = model.getDateType();
|
||||
|
||||
DateType type;
|
||||
switch (dateType) {
|
||||
case TemplateDateModel.DATE:
|
||||
type = DateType.DATE;
|
||||
break;
|
||||
case TemplateDateModel.DATETIME:
|
||||
type = DateType.DATETIME;
|
||||
break;
|
||||
case TemplateDateModel.TIME:
|
||||
type = DateType.TIME;
|
||||
break;
|
||||
default:
|
||||
type = DateType.UNKNOWN;
|
||||
}
|
||||
map.put(Key.DATE_TYPE.toString(), type);
|
||||
|
||||
map.put(Key.VALUE.toString(), model.getAsDate());
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateHashModel model) throws TemplateModelException {
|
||||
// The data model is a hash; when else do we get here?
|
||||
log.debug("Dumping model " + model);
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.HASH);
|
||||
//map.put(Key.VALUE.toString(), ????);
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateSequenceModel model) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.SEQUENCE);
|
||||
int itemCount = model.size();
|
||||
List<Map<String, Object>> items = new ArrayList<Map<String, Object>>(itemCount);
|
||||
for ( int i = 0; i < itemCount; i++ ) {
|
||||
TemplateModel item = model.get(i);
|
||||
items.add(getDump(item));
|
||||
}
|
||||
map.put(Key.VALUE.toString(), items);
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateHashModelEx model) throws TemplateModelException {
|
||||
Object unwrappedModel = DeepUnwrap.permissiveUnwrap(model);
|
||||
// This seems to be the most reliable way of distinguishing a wrapped map from a wrapped object.
|
||||
// A map may be wrapped as a SimpleHash, and an object may be wrapped as a StringModel, but they could
|
||||
// be wrapped as other types as well.
|
||||
if ( unwrappedModel instanceof Map ) {
|
||||
return getMapDump(model);
|
||||
}
|
||||
|
||||
// Java objects are wrapped as TemplateHashModelEx-s.
|
||||
return getObjectDump(model, unwrappedModel);
|
||||
}
|
||||
|
||||
private Map<String, Object> getMapDump(TemplateHashModelEx model) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.HASH_EX);
|
||||
SortedMap<String, Object> items = new TreeMap<String, Object>();
|
||||
TemplateCollectionModel keys = model.keys();
|
||||
TemplateModelIterator iModel = keys.iterator();
|
||||
while (iModel.hasNext()) {
|
||||
String key = iModel.next().toString();
|
||||
// Work around this oddity: model.object does not contain
|
||||
// values for "empty" and "keys", but model.keys() does.
|
||||
if ("class".equals(key) || "empty".equals(key)) {
|
||||
continue;
|
||||
}
|
||||
TemplateModel value = model.get(key);
|
||||
// A map with exposed methods includes methods inherited from Map and Object like
|
||||
// size(), getClass(), etc. Punt on these for now by suppressing in the dump,
|
||||
// though this is not the optimal solution. If they are exposed to the templates,
|
||||
// the dump should also expose them. I'm guessing that in most cases these
|
||||
// methods are not relevant to template authors.
|
||||
if (! (value instanceof TemplateMethodModel)) {
|
||||
items.put(key, getDump(value));
|
||||
}
|
||||
|
||||
}
|
||||
map.put(Key.VALUE.toString(), items);
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getObjectDump(TemplateHashModelEx model, Object object) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), object.getClass().getName());
|
||||
|
||||
if( object instanceof java.lang.reflect.Method)
|
||||
return map;
|
||||
|
||||
// Compile the collections of properties and methods available to the template
|
||||
SortedMap<String, Object> properties = new TreeMap<String, Object>();
|
||||
SortedMap<String, Object> methods = new TreeMap<String, Object>();
|
||||
|
||||
// keys() gets only values visible to template based on the BeansWrapper used.
|
||||
// Note: if the BeansWrapper exposure level > BeansWrapper.EXPOSE_PROPERTIES_ONLY,
|
||||
// keys() returns both method and property name for any available method with no
|
||||
// parameters: e.g., both name and getName(). We are going to eliminate the latter.
|
||||
TemplateCollectionModel keys = model.keys();
|
||||
TemplateModelIterator iModel = keys.iterator();
|
||||
|
||||
// Create a Set from keys so we can use the Set API.
|
||||
Set<String> keySet = new HashSet<String>();
|
||||
while (iModel.hasNext()) {
|
||||
String key = iModel.next().toString();
|
||||
keySet.add(key);
|
||||
}
|
||||
|
||||
if (keySet.size() > 0) {
|
||||
|
||||
Class<?> cls = object.getClass();
|
||||
Method[] classMethods = cls.getMethods();
|
||||
|
||||
// Iterate through the methods rather than the keys, so that we can remove
|
||||
// some keys based on reflection on the methods. We also want to remove duplicates
|
||||
// like name/getName - we'll keep only the first form.
|
||||
for ( Method method : classMethods ) {
|
||||
|
||||
if( "declaringClass".equals( method.getName() ))
|
||||
continue;
|
||||
|
||||
// Eliminate methods declared on Object
|
||||
// and other unusual places that can cause problems.
|
||||
Class<?> c = method.getDeclaringClass();
|
||||
|
||||
if (c == null ||
|
||||
c.equals(java.lang.Object.class) ||
|
||||
c.equals(java.lang.reflect.Constructor.class) ||
|
||||
c.equals(java.lang.reflect.Field.class ) )
|
||||
continue;
|
||||
if(
|
||||
c.getPackage().getName().startsWith("sun.") ||
|
||||
c.getPackage().getName().startsWith("java.lang") ||
|
||||
c.getPackage().getName().startsWith("java.security") )
|
||||
continue;
|
||||
|
||||
// Eliminate deprecated methods
|
||||
if (method.isAnnotationPresent(Deprecated.class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Include only methods included in keys(). This factors in visibility
|
||||
// defined by the model's BeansWrapper.
|
||||
String methodName = method.getName();
|
||||
|
||||
Matcher matcher = PROPERTY_NAME_PATTERN.matcher(methodName);
|
||||
// If the method name starts with "get" or "is", check if it's available
|
||||
// as a property
|
||||
if (matcher.find()) {
|
||||
String propertyName = getPropertyName(methodName);
|
||||
|
||||
// The method is available as a property
|
||||
if (keySet.contains(propertyName)) {
|
||||
try{
|
||||
TemplateModel value = model.get(propertyName);
|
||||
properties.put(propertyName, getDump(value));
|
||||
}catch(Throwable th){
|
||||
log.error("problem dumping " + propertyName + " on " + object.getClass().getName() +
|
||||
" declared in " + c.getName(), th);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Else look for the entire methodName in the key set, to include
|
||||
// those that are exposed as methods rather than properties.
|
||||
if (keySet.contains(methodName)) {
|
||||
String methodDisplayName = getMethodDisplayName(method);
|
||||
// If no arguments, invoke the method to get the result
|
||||
if ( methodDisplayName.endsWith("()") ) {
|
||||
SimpleMethodModel methodModel = (SimpleMethodModel)model.get(methodName);
|
||||
try {
|
||||
Object result = methodModel.exec(null);
|
||||
ObjectWrapper wrapper = getWrapper(model);
|
||||
TemplateModel wrappedResult = wrapper.wrap(result);
|
||||
methods.put(methodDisplayName, getDump(wrappedResult));
|
||||
} catch (Exception e) {
|
||||
log.error(e, e);
|
||||
}
|
||||
// Else display method name, parameter types, and return type
|
||||
} else {
|
||||
String returnTypeName = getReturnTypeName(method);
|
||||
Map<String, String> methodValue = new HashMap<String, String>();
|
||||
if ( ! returnTypeName.equals("void") ) {
|
||||
methodValue.put(Key.TYPE.toString(), returnTypeName);
|
||||
}
|
||||
methods.put(methodDisplayName, methodValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> objectValue = new HashMap<String, Object>(2);
|
||||
objectValue.put(Key.PROPERTIES.toString(), properties);
|
||||
objectValue.put(Key.METHODS.toString(), methods);
|
||||
|
||||
map.put(Key.VALUE.toString(), objectValue);
|
||||
return map;
|
||||
}
|
||||
|
||||
private ObjectWrapper getWrapper(TemplateHashModelEx model) {
|
||||
// Attempt to find the wrapper that this template model object was wrapped with.
|
||||
if (model instanceof BeanModel) {
|
||||
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 {
|
||||
return new BeansWrapper();
|
||||
}
|
||||
}
|
||||
|
||||
private String getMethodDisplayName(Method method) {
|
||||
String methodName = method.getName();
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
List<String> paramTypeList = new ArrayList<String>(paramTypes.length);
|
||||
if (paramTypes.length > 0) {
|
||||
for (Class<?> cls : paramTypes) {
|
||||
paramTypeList.add(getSimpleTypeName(cls));
|
||||
}
|
||||
}
|
||||
methodName += "(" + StringUtils.join(paramTypeList, ", ") + ")";
|
||||
return methodName;
|
||||
}
|
||||
|
||||
private String getReturnTypeName(Method method) {
|
||||
Class<?> cls = method.getReturnType();
|
||||
Package pkg = cls.getPackage();
|
||||
if (pkg != null) { // void return type has null package
|
||||
String packageName = pkg.getName();
|
||||
if (packageName.startsWith("java")) {
|
||||
return getSimpleTypeName(cls);
|
||||
}
|
||||
}
|
||||
return cls.getName();
|
||||
}
|
||||
|
||||
private String getSimpleTypeName(Class<?> cls) {
|
||||
return cls.getSimpleName().replace("[]", "s");
|
||||
}
|
||||
|
||||
// Return the method name as it is represented in TemplateHashModelEx.keys()
|
||||
private String getPropertyName(String methodName) {
|
||||
String keyName = methodName.replaceAll("^(get|is)", "");
|
||||
return StringUtils.uncapitalize(keyName);
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateCollectionModel model) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.COLLECTION);
|
||||
List<Map<String, Object>> items = new ArrayList<Map<String, Object>>();
|
||||
TemplateModelIterator iModel = model.iterator();
|
||||
while (iModel.hasNext()) {
|
||||
TemplateModel m = iModel.next();
|
||||
items.add(getDump(m));
|
||||
}
|
||||
map.put(Key.VALUE.toString(), items);
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateMethodModel model, String varName) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.METHOD);
|
||||
map.put(Key.CLASS.toString(), model.getClass().getName());
|
||||
map.put(Key.HELP.toString(), getHelp(model, varName));
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateDirectiveModel model, String varName) throws TemplateModelException {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put(Key.TYPE.toString(), Type.DIRECTIVE);
|
||||
map.put(Key.CLASS.toString(), model.getClass().getName());
|
||||
map.put(Key.HELP.toString(), getHelp(model, varName));
|
||||
return map;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, Object> getHelp(TemplateModel model, String varName) {
|
||||
if ( model instanceof TemplateMethodModel || model instanceof TemplateDirectiveModel ) {
|
||||
String modelClass = model instanceof TemplateMethodModel ? "TemplateMethodModel" : "TemplateDirectiveModel";
|
||||
Class<?> cls = model.getClass();
|
||||
try {
|
||||
Method help = cls.getMethod("help", String.class);
|
||||
try {
|
||||
return (Map<String, Object>) help.invoke(model, varName);
|
||||
} catch (ClassCastException e) {
|
||||
log.error("Method help() of " + modelClass + " of class " + cls.getName() + " has incorrect return type.");
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("Error invoking method help() on " + modelClass + " of class " + cls.getName());
|
||||
return null;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
log.info("No help() method defined for " + modelClass + " of class " + cls.getName());
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("Error getting method help() for " + modelClass + " " + cls.getName());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Map<String, Object> getTemplateModelDump(TemplateModel model) throws TemplateModelException {
|
||||
// One of the more specific cases should have applied. Track whether this actually occurs.
|
||||
log.debug("Found template model of type " + model.getClass().getName());
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
Object unwrappedModel = DeepUnwrap.permissiveUnwrap(model);
|
||||
map.put(Key.TYPE.toString(), unwrappedModel.getClass().getName());
|
||||
map.put(Key.VALUE.toString(), unwrappedModel.toString());
|
||||
return map;
|
||||
}
|
||||
|
||||
protected void dump(Map<String, Object> dump, Environment env, String title)
|
||||
throws TemplateException, IOException {
|
||||
dump(dump, env, title, TEMPLATE_DEFAULT);
|
||||
}
|
||||
|
||||
protected void dump(Map<String, Object> dump, Environment env, String title, String templateName)
|
||||
throws TemplateException, IOException {
|
||||
|
||||
// Wrap the dump in another map so the template has a handle to iterate through
|
||||
// the values: <#list dump?keys as key>...</#list>
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
|
||||
//put the TemplateModel to be dumped at 'dumpValue'
|
||||
map.put("dumpValue", dump);
|
||||
map.put("title", title);
|
||||
writeDump(map, env, templateName);
|
||||
}
|
||||
|
||||
protected void writeDump(Map<String, Object> map, Environment env, String templateName)
|
||||
throws TemplateException, IOException {
|
||||
|
||||
//bdc34: not sure what to do there to
|
||||
//get the scope from the env to this dump template, trying an ugly copy
|
||||
map.putAll( toMap( env.getDataModel() ));
|
||||
|
||||
Template template = env.getConfiguration().getTemplate(templateName);
|
||||
StringWriter sw = new StringWriter();
|
||||
template.process(map, sw);
|
||||
Writer out = env.getOut();
|
||||
out.write(sw.toString());
|
||||
|
||||
}
|
||||
|
||||
public Map<String, Object> help(String name) {
|
||||
return new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Freemarker TemplateObject to a usable map.
|
||||
* @throws TemplateModelException
|
||||
*/
|
||||
protected Map<? extends String, ? extends Object> toMap( Object hash ) throws TemplateModelException{
|
||||
Map<String,Object> outMap = new HashMap<String,Object>();
|
||||
|
||||
if( hash instanceof TemplateHashModelEx ){
|
||||
TemplateHashModelEx thme = (TemplateHashModelEx) hash;
|
||||
for( String key : junkToStrings( thme.keys() )){
|
||||
outMap.put(key, thme.get(key));
|
||||
}
|
||||
}else{
|
||||
log.error("Freemarker is passing odd objects to method toMap(): " + hash.getClass().getName());
|
||||
}
|
||||
|
||||
return outMap;
|
||||
}
|
||||
|
||||
protected List<String> junkToStrings( TemplateCollectionModel junk ){
|
||||
List<String> keys = new ArrayList<String>();
|
||||
try{
|
||||
TemplateModelIterator it = junk.iterator();
|
||||
while( it.hasNext() ){
|
||||
Object obj = it.next();
|
||||
if( obj instanceof StringModel){
|
||||
keys.add( ((StringModel)obj).getAsString() );
|
||||
}else if( obj instanceof SimpleScalar ){
|
||||
keys.add( ((SimpleScalar)obj).getAsString());
|
||||
}else{
|
||||
log.error("Freemarker is setting keys to hashes as non-strings: " + obj.getClass().getName());
|
||||
}
|
||||
}
|
||||
}catch(Exception ex){
|
||||
log.error("Freemarker is messing with us",ex);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
}
|
82
api/src/main/java/freemarker/ext/dump/DumpAllDirective.java
Normal file
82
api/src/main/java/freemarker/ext/dump/DumpAllDirective.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package freemarker.ext.dump;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.TemplateDirectiveBody;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.TemplateHashModel;
|
||||
import freemarker.template.TemplateModel;
|
||||
import freemarker.template.TemplateModelException;
|
||||
import freemarker.template.utility.DeepUnwrap;
|
||||
|
||||
public class DumpAllDirective extends BaseDumpDirective {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final Log log = LogFactory.getLog(DumpDirective.class);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void execute(Environment env, Map params, TemplateModel[] loopVars,
|
||||
TemplateDirectiveBody body) throws TemplateException, IOException {
|
||||
|
||||
if (params.size() != 0) {
|
||||
throw new TemplateModelException(
|
||||
"The dumpAll directive doesn't allow parameters.");
|
||||
}
|
||||
|
||||
if (loopVars.length != 0) {
|
||||
throw new TemplateModelException(
|
||||
"The dump directive doesn't allow loop variables.");
|
||||
}
|
||||
|
||||
if (body != null) {
|
||||
throw new TemplateModelException(
|
||||
"The dump directive doesn't allow nested content.");
|
||||
}
|
||||
|
||||
SortedMap<String, Object> dump = getDataModelDump(env);
|
||||
String title = "Template data model dump for " + env.getTemplate().getName();
|
||||
dump(dump, env, title);
|
||||
}
|
||||
|
||||
SortedMap<String, Object> getDataModelDump(Environment env) throws TemplateModelException {
|
||||
SortedMap<String, Object> dump = new TreeMap<String, Object>();
|
||||
TemplateHashModel dataModel = env.getDataModel();
|
||||
// Need to unwrap in order to iterate through the variables
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> unwrappedDataModel = (Map<String, Object>) DeepUnwrap.permissiveUnwrap(dataModel);
|
||||
List<String> varNames = new ArrayList<String>(unwrappedDataModel.keySet());
|
||||
|
||||
for (String varName : varNames) {
|
||||
dump.putAll(getTemplateVariableDump(varName, dataModel.get(varName)));
|
||||
}
|
||||
|
||||
return dump;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> help(String name) {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
map.put("effect", "Dumps the contents of the template data model.");
|
||||
|
||||
List<String> examples = new ArrayList<String>();
|
||||
examples.add("<@" + name + " />");
|
||||
map.put("examples", examples);
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
76
api/src/main/java/freemarker/ext/dump/DumpDirective.java
Normal file
76
api/src/main/java/freemarker/ext/dump/DumpDirective.java
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package freemarker.ext.dump;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.SimpleScalar;
|
||||
import freemarker.template.TemplateDirectiveBody;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.TemplateModel;
|
||||
import freemarker.template.TemplateModelException;
|
||||
|
||||
public class DumpDirective extends BaseDumpDirective {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final Log log = LogFactory.getLog(DumpDirective.class);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void execute(Environment env, Map params, TemplateModel[] loopVars,
|
||||
TemplateDirectiveBody body) throws TemplateException, IOException {
|
||||
|
||||
if (loopVars.length != 0) {
|
||||
throw new TemplateModelException(
|
||||
"The dump directive doesn't allow loop variables.");
|
||||
}
|
||||
if (body != null) {
|
||||
throw new TemplateModelException(
|
||||
"The dump directive doesn't allow nested content.");
|
||||
}
|
||||
|
||||
Object o = params.get("var");
|
||||
|
||||
if ( o == null) {
|
||||
throw new TemplateModelException(
|
||||
"Must specify 'var' argument.");
|
||||
}
|
||||
|
||||
if ( !(o instanceof SimpleScalar)) {
|
||||
throw new TemplateModelException(
|
||||
"Value of parameter 'var' must be a string.");
|
||||
}
|
||||
|
||||
String varName = o.toString(); //((SimpleScalar)o).getAsString();
|
||||
Map<String, Object> map = getTemplateVariableDump(varName, env);
|
||||
String title = "Template variable dump";
|
||||
dump(map, env, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> help(String name) {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
map.put("effect", "Dumps the contents of a template variable.");
|
||||
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("var", "name of variable to dump");
|
||||
map.put("parameters", params);
|
||||
|
||||
List<String> examples = new ArrayList<String>();
|
||||
examples.add("<@" + name + " var=\"urls\" />");
|
||||
map.put("examples", examples);
|
||||
|
||||
return map;
|
||||
|
||||
}
|
||||
}
|
83
api/src/main/java/freemarker/ext/dump/HelpDirective.java
Normal file
83
api/src/main/java/freemarker/ext/dump/HelpDirective.java
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package freemarker.ext.dump;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.SimpleScalar;
|
||||
import freemarker.template.TemplateDirectiveBody;
|
||||
import freemarker.template.TemplateDirectiveModel;
|
||||
import freemarker.template.TemplateException;
|
||||
import freemarker.template.TemplateHashModel;
|
||||
import freemarker.template.TemplateMethodModel;
|
||||
import freemarker.template.TemplateModel;
|
||||
import freemarker.template.TemplateModelException;
|
||||
|
||||
public class HelpDirective extends BaseDumpDirective {
|
||||
|
||||
@Override
|
||||
public void execute(Environment env, Map params, TemplateModel[] loopVars,
|
||||
TemplateDirectiveBody body) throws TemplateException, IOException {
|
||||
|
||||
if (loopVars.length != 0) {
|
||||
throw new TemplateModelException(
|
||||
"The help directive doesn't allow loop variables.");
|
||||
}
|
||||
if (body != null) {
|
||||
throw new TemplateModelException(
|
||||
"The help directive doesn't allow nested content.");
|
||||
}
|
||||
|
||||
Object o = params.get("for");
|
||||
|
||||
if ( o == null) {
|
||||
throw new TemplateModelException(
|
||||
"Must specify 'for' argument.");
|
||||
}
|
||||
|
||||
if ( !(o instanceof SimpleScalar)) {
|
||||
throw new TemplateModelException(
|
||||
"Value of parameter 'for' must be a string.");
|
||||
}
|
||||
|
||||
String varName = o.toString(); //((SimpleScalar)o).getAsString();
|
||||
TemplateHashModel dataModel = env.getDataModel();
|
||||
Object templateModel = dataModel.get(varName);
|
||||
|
||||
if (! (templateModel instanceof TemplateMethodModel || templateModel instanceof TemplateDirectiveModel)) {
|
||||
throw new TemplateModelException(
|
||||
"Value of parameter '" + varName + "' must be the name of a directive or method");
|
||||
}
|
||||
|
||||
Map<String, Object> map = getTemplateVariableDump(varName, env);
|
||||
|
||||
String type = templateModel instanceof TemplateMethodModel ? "method" : "directive";
|
||||
String title = "Template " + type + " help";
|
||||
dump(map, env, title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> help(String name) {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
|
||||
map.put("effect", "Outputs help for a directive or method.");
|
||||
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("for", "name of directive or method");
|
||||
map.put("parameters", params);
|
||||
|
||||
List<String> examples = new ArrayList<String>();
|
||||
examples.add("<@" + name + " for=\"dump\" />");
|
||||
examples.add("<@" + name + " for=\"profileUrl\" />");
|
||||
map.put("examples", examples);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue