Or... maybe you don't really need the Java type name, only to show info for writing templates? Then:
public final class TemplateLanguageTypeNameMethod implements TemplateMethodModelEx { @Override public Object exec(List/*<TemplateModel>*/ arguments) throws TemplateModelException { if (arguments.size() != 1) { throw new TemplateModelException("javaType method must be called with one argument."); } return ClassUtil.getFTLTypeDescription((TemplateModel) arguments.get(0)); } } On Sat, Feb 17, 2024 at 2:50 PM Daniel Dekany <daniel.dek...@gmail.com> wrote: > So you need the list of the top-level variables in your data-model (aka. > template context). With their Java type name, and name. > > The basic ideas is this: > > <#list .data_model as k, v> > ${javaTypeName(v)}: ${k} > </#list> > > <#-- For now just let's have this: > > <#function javaTypeName x><#return "TODO"></#function> > > See if it lists all the names that you want to see. Because, the data > model can use fallbacks, and those are (certainly) not listed. So that's > one complication to get over. > > The really tricky part is implementing javaTypeName. The template > language has its own type system, represented by the TemplatModel > interface, and its numerous subclasses (and quite a lot of > accumulated historical complications). So when you are inside the template, > it's not trivial to tell what the plain Java type of a value is. Strictly > speaking, it's impossible to tell, because you can't know what custom > ObjectWrapper was used. (That gives flexibility, but is disadvantageous in > this use case, among others.) Also, some TemplateModel-s do not wrap any > "plain" Java object at all; they were just constructed to be used inside > templates, but then, I think the Java type to show is simply the class that > implements TemplateModel. But, if I only consider what most people use, I > came up with the following monstrosity (untested, and probably misses some > nuances): > > ---------- > public final class JavaTypeNameMethod implements TemplateMethodModelEx { > private static final Package JAVA_LANG_PACKAGE = > String.class.getPackage(); > > @Override > public Object exec(List/*<TemplateModel>*/ arguments) throws > TemplateModelException { > if (arguments.size() != 1) { > throw new TemplateModelException("javaType method must be > called with one argument."); > } > > TemplateModel ftlValue = (TemplateModel) arguments.get(0); > if (ftlValue == null) { > return "null"; > } > > Object javaValue; > if (ftlValue instanceof WrapperTemplateModel) { > javaValue = ((WrapperTemplateModel) > ftlValue).getWrappedObject(); > } else { > if (ftlValue instanceof SimpleScalar) { > return getClassName(String.class); > } else if (ftlValue == TemplateBooleanModel.TRUE || ftlValue > == TemplateBooleanModel.FALSE) { > return getClassName(boolean.class); > } else if (ftlValue instanceof SimpleNumber) { > javaValue = ((SimpleNumber) ftlValue).getAsNumber(); > } else if (ftlValue instanceof SimpleDate) { > javaValue = ((SimpleDate) ftlValue).getAsDate(); > } else if (ftlValue instanceof SimpleHash) { > return getClassName(Map.class); > } else if (ftlValue instanceof SimpleSequence) { > return getClassName(List.class); > } else if (ftlValue instanceof SimpleCollection) { > return getClassName(Iterable.class); > } else if (ftlValue instanceof SimpleMethodModel) { > return getClassName(Method.class); > } else { > // Will just show the TemplateModel implementation class > itself, because some TemplateModels just don't wrap anything. > javaValue = ftlValue; > } > } > > if (javaValue == null) { > return "null"; > } > > Class<?> javaValueClass = javaValue.getClass(); > return getClassName(javaValueClass); > } > > /** > * Central place to decide if we use full qualified name. > */ > private static String getClassName(Class<?> javaValueClass) { > return javaValueClass.getPackage().equals(JAVA_LANG_PACKAGE) > ? javaValueClass.getSimpleName() > : javaValueClass.getName(); > } > } > ---------- > > And then you create an instance of this, and put it into the data-model. > Or you can just use this in the template, in place of the earlier > <#function ....>, but ?new is not allowed in all projects: > > <#assign javaTypeName = "com.example.JavaTypeNameMethod"?new()> > > BTW, I recommend StackOverflow for user questions, not this list. More > likely that you get an answer, and way more people will find the answer > later. > -- Best regards, Daniel Dekany