freemarker.ext.dump.BaseDumpDirective Maven / Gradle / Ivy
The newest version!
/* $This file is distributed under the terms of the license in LICENSE$ */
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.lang3.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.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 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 getTemplateVariableDump(String varName, TemplateModel valueToDump)
throws TemplateModelException {
Map value = new HashMap();
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 dump = new HashMap();
dump.put(varName, value);
return dump;
}
private Map getDump(TemplateModel valueToDump) throws TemplateModelException {
Map map = new HashMap();
// 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 getTemplateModelDump(TemplateScalarModel model) throws TemplateModelException {
Map map = new HashMap();
map.put(Key.TYPE.toString(), Type.STRING);
map.put(Key.VALUE.toString(), model.getAsString());
return map;
}
private Map getTemplateModelDump(TemplateBooleanModel model) throws TemplateModelException {
Map map = new HashMap();
map.put(Key.TYPE.toString(), Type.BOOLEAN);
map.put(Key.VALUE.toString(), model.getAsBoolean());
return map;
}
private Map getTemplateModelDump(TemplateNumberModel model) throws TemplateModelException {
Map map = new HashMap();
map.put(Key.TYPE.toString(), Type.NUMBER);
map.put(Key.VALUE.toString(), model.getAsNumber());
return map;
}
private Map getTemplateModelDump(TemplateDateModel model) throws TemplateModelException {
Map map = new HashMap();
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 getTemplateModelDump(TemplateHashModel model) throws TemplateModelException {
// The data model is a hash; when else do we get here?
log.debug("Dumping model " + model);
Map map = new HashMap();
map.put(Key.TYPE.toString(), Type.HASH);
//map.put(Key.VALUE.toString(), ????);
return map;
}
private Map getTemplateModelDump(TemplateSequenceModel model) throws TemplateModelException {
Map map = new HashMap();
map.put(Key.TYPE.toString(), Type.SEQUENCE);
int itemCount = model.size();
List