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 <[email protected]>
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