| <!doctype html> |
| <!-- Generated by FreeMarker/Docgen from DocBook --> |
| <html lang="en" class="page-type-section"> |
| <head prefix="og: http://ogp.me/ns#"> |
| <meta charset="utf-8"> |
| <title>Object wrappers - Apache FreeMarker Manual</title> |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| <meta name="viewport" content="width=device-width,initial-scale=1"> |
| <meta name="format-detection" content="telephone=no"> |
| <meta property="og:site_name" content="Apache FreeMarker Manual"> |
| <meta property="og:title" content="Object wrappers"> |
| <meta property="og:locale" content="en_US"> |
| <meta property="og:url" content="https://freemarker.apache.org/docs/pgui_datamodel_objectWrapper.html"> |
| <link rel="canonical" href="https://freemarker.apache.org/docs/pgui_datamodel_objectWrapper.html"> |
| <link rel="icon" href="favicon.png" type="image/png"> |
| <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:500,700,400,300|Droid+Sans+Mono"> |
| <link rel="stylesheet" type="text/css" href="docgen-resources/docgen.min.css?1707770044859"> |
| <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/cookie-bar/cookiebar-latest.min.js"></script> |
| </head> |
| <body itemscope itemtype="https://schema.org/Code"> |
| <meta itemprop="url" content="https://freemarker.apache.org/docs/"> |
| <meta itemprop="name" content="Apache FreeMarker Manual"> |
| |
| <!--[if lte IE 9]> |
| <div class="oldBrowserWarning" style="display: block"> |
| Unsupported web browser - Use a modern browser to view this website! |
| </div> |
| <![endif]--> <div class="oldBrowserWarning"> |
| Unsupported web browser - Use a modern browser to view this website! |
| </div> |
| <div class="header-top-bg"><div class="site-width header-top"><div id="hamburger-menu" role="button"></div> <div class="logo"> |
| <a href="https://freemarker.apache.org" role="banner"><img itemprop="image" src="logo.png" alt="FreeMarker"></a> </div> |
| <ul class="tabs"><li><a href="https://freemarker.apache.org/">Home</a></li><li class="current"><a href="index.html">Manual</a></li><li><a class="external" href="api/index.html">Java API</a></li></ul><ul class="secondary-tabs"><li><a class="tab icon-heart" href="https://freemarker.apache.org/contribute.html" title="Contribute"><span>Contribute</span></a></li><li><a class="tab icon-bug" href="https://issues.apache.org/jira/projects/FREEMARKER" title="Report a Bug"><span>Report a Bug</span></a></li><li><a class="tab icon-download" href="https://freemarker.apache.org/freemarkerdownload.html" title="Download"><span>Download</span></a></li></ul></div></div><div class="header-bottom-bg"><div class="site-width search-row"><a href="index.html" class="navigation-header">Manual</a><div class="navigation-header"></div><form method="get" class="search-form" action="search-results.html"><fieldset><legend class="sr-only">Search form</legend><label for="search-field" class="sr-only">Search query</label><input id="search-field" name="q" type="search" class="search-input" placeholder="Search" spellcheck="false" autocorrect="off" autocomplete="off"><button type="submit" class="search-btn"><span class="sr-only">Search</span></button></fieldset></form></div><div class="site-width breadcrumb-row"> <div class="breadcrumbs"> |
| <ul class="breadcrumb" itemscope itemtype="http://schema.org/BreadcrumbList"><li class="step-0" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="index.html"><span itemprop="name">Apache FreeMarker Manual</span></a></li><li class="step-1" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="pgui.html"><span itemprop="name">Programmer's Guide</span></a></li><li class="step-2" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="pgui_datamodel.html"><span itemprop="name">The Data Model</span></a></li><li class="step-3" itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"><a class="label" itemprop="item" href="pgui_datamodel_objectWrapper.html"><span itemprop="name">Object wrappers</span></a></li></ul> </div> |
| <div class="bookmarks" title="Bookmarks"><span class="sr-only">Bookmarks:</span><ul><li><a href="alphaidx.html">Alpha. index</a></li><li><a href="gloss.html">Glossary</a></li><li><a href="dgui_template_exp.html#exp_cheatsheet">Expressions</a></li><li><a href="ref_builtins_alphaidx.html">?builtins</a></li><li><a href="ref_directive_alphaidx.html">#directives</a></li><li><a href="ref_specvar.html">.spec_vars</a></li><li><a href="app_faq.html">FAQ</a></li></ul></div></div></div> <div class="main-content site-width"> |
| <div class="content-wrapper"> |
| <div id="table-of-contents-wrapper" class="col-left"> |
| <script>var breadcrumb = ["Apache FreeMarker Manual","Programmer\'s Guide","The Data Model","Object wrappers"];</script> |
| <script src="toc.js?1707770044859"></script> |
| <script src="docgen-resources/main.min.js?1707770044859"></script> |
| </div> |
| <div class="col-right"><div class="page-content"><div class="page-title"><div class="pagers top"><a class="paging-arrow previous" href="pgui_datamodel_node.html"><span>Previous</span></a><a class="paging-arrow next" href="pgui_config.html"><span>Next</span></a></div><div class="title-wrapper"> |
| <h1 class="content-header header-section1" id="pgui_datamodel_objectWrapper" itemprop="headline">Object wrappers</h1> |
| </div></div><div class="page-menu"> |
| <div class="page-menu-title">Page Contents</div> |
| <ul><li><a class="page-menu-link" href="#pgui_datamodel_defaultObjectWrapper" data-menu-target="pgui_datamodel_defaultObjectWrapper">The default object wrapper</a></li><li><a class="page-menu-link" href="#pgui_datamodel_customObjectWrappingExample" data-menu-target="pgui_datamodel_customObjectWrappingExample">Custom object wrapping example</a></li></ul> </div><p>The object wrapper is an object that implements the |
| <code class="inline-code">freemarker.template.ObjectWrapper</code> interface. It's |
| purpose is to implement a mapping between Java objects (like |
| <code class="inline-code">String</code>-s, <code class="inline-code">Map</code>-s, |
| <code class="inline-code">List</code>-s, instances of your application specific |
| classes, etc.) and FTL's type system. With other words, it specifies |
| how the templates will see the Java objects of the data-model |
| (including the return value of Java methods called from the template). |
| The object wrapper is plugged into the |
| <code class="inline-code">Configuration</code> as its |
| <code class="inline-code">object_wrapper</code> setting (or with |
| <code class="inline-code">Configuration.setObjectWrapper</code>).</p><p>FTL's type system is technically represented by the |
| <code class="inline-code">TemplateModel</code> sub-interfaces that were introduced |
| earlier (<code class="inline-code">TemplateScalarModel</code>, |
| <code class="inline-code">TemplateHashModel</code>, |
| <code class="inline-code">TemplateSequenceModel</code>, etc). To map a Java object |
| to FTL's type system, object wrapper's <code class="inline-code">TemplateModel |
| wrap(java.lang.Object obj)</code> method will be called.</p><p>Sometimes FreeMarker needs to reverse this mapping, in which |
| case the <code class="inline-code">ObjectWrapper</code>'s <code class="inline-code">Object |
| unwrap(TemplateModel)</code> method is called (or some other |
| variation of that, but see the API documentation for such details). |
| This last operation is in |
| <code class="inline-code">ObjectWrapperAndUnwrapper</code>, the subinterface of |
| <code class="inline-code">ObjectWrapper</code>. Most real world object wrappers will |
| implement <code class="inline-code">ObjectWrapperAndUnwrapper</code>.</p><p>Here's how wrapping Java objects that contain other objects |
| (like a <code class="inline-code">Map</code>, a <code class="inline-code">List</code>, an array, |
| or an object with some JavaBean properties) usually work. Let's say, |
| an object wrapper wraps an <code class="inline-code">Object[]</code> array into some |
| implementation of the <code class="inline-code">TemplateSquenceModel</code> |
| interface. When FreeMarker needs an item from that FTL sequence, it |
| will call <code class="inline-code">TemplateSquenceModel.get(int index)</code>. The |
| return type of this method is <code class="inline-code">TemplateModel</code>, that |
| is, the <code class="inline-code">TemplateSquenceModel</code> implementation not |
| only have to get the <code class="inline-code">Object</code> from the given index of |
| the array, it's also responsible for wrapping that value before |
| returning it. To solve that, a typical |
| <code class="inline-code">TemplateSquenceModel</code> implementation will store the |
| <code class="inline-code">ObjectWrapper</code> that has cerated it, and then invoke |
| that <code class="inline-code">ObjectWrapper</code> to wrap the contained value. The |
| same logic stands for <code class="inline-code">TemplateHashModel</code> or for any |
| other <code class="inline-code">TemplateModel</code> that's a container for further |
| <code class="inline-code">TemplateModel</code>-s. Hence, usually, no mater how deep |
| the value hierarchy is, all values will be wrapped by the same single |
| <code class="inline-code">ObjectWrapper</code>. (To create |
| <code class="inline-code">TemplateModel</code> implementations that follow this |
| idiom, you can use the |
| <code class="inline-code">freemarker.template.WrappingTemplateModel</code> as base |
| class.)</p><p>The data-model itself (the root variable) is a |
| <code class="inline-code">TemplateHashModel</code>. The root object that you specify |
| to <code class="inline-code">Template.process</code> will be wrapped with the object |
| wrapper specified in the <code class="inline-code">object_wrapper</code> |
| configuration setting, which must yield a |
| <code class="inline-code">TemplateHashModel</code>. From then on, the wrapping of |
| the contained values follow the logic described earlier (i.e., the |
| container is responsible for wrapping its children).</p><p>Well behaving object wrappers bypass objects that already |
| implement <code class="inline-code">TemplateModel</code> as is. So if you put an |
| object into the data-model that already implements |
| <code class="inline-code">TemplateModel</code> (or you return as such object from a |
| Java method that's called from the template, etc.), then you can avoid |
| actual object wrapping. You do this usually when you are creating a |
| value specifically to be accessed from a template. Thus, you avoid |
| much of the object wrapping performance overhead, also you can control |
| exactly what will the template see (not depending on the mapping |
| strategy of the current object wrapper). A frequent application of |
| this trick is using a |
| <code class="inline-code">freemarker.template.SimpleHash</code> as the data-model |
| root (rather than a <code class="inline-code">Map</code>), by filling it with |
| <code class="inline-code">SimpleHash</code>'s <code class="inline-code">put</code> method (that's |
| important, so it won't have to copy an existing <code class="inline-code">Map</code> |
| that you have already filled). This speeds up top-level data-model |
| variable access.</p> |
| |
| |
| |
| |
| <h2 class="content-header header-section2" id="pgui_datamodel_defaultObjectWrapper">The default object wrapper</h2> |
| |
| |
| |
| |
| |
| |
| <p>The default of the <code class="inline-code">object_wrapper</code> |
| <code class="inline-code">Configuration</code> setting is a |
| <code class="inline-code">freemarker.template.DefaultObjectWrapper</code> |
| singleton. Unless you have very special requirements, it's |
| recommended to use this object wrapper, or an instance of a |
| <code class="inline-code">DefaultObjectWrapper</code> subclass of yours.</p> |
| |
| <p>It recognizes most basic Java types, like |
| <code class="inline-code">String</code>, <code class="inline-code">Number</code>, |
| <code class="inline-code">Boolean</code>, <code class="inline-code">Date</code>, |
| <code class="inline-code">List</code> (and in general all kind of |
| <code class="inline-code">java.util.Collection</code>-s), arrays, |
| <code class="inline-code">Map</code>, etc., and wraps them into the naturally |
| matching <code class="inline-code">TemplateModel</code> interfaces. It will also |
| wrap W3C DOM nodes with |
| <code class="inline-code">freemarker.ext.dom.NodeModel</code>, so you can |
| conveniently traverse XML as <a href="xgui.html">described in its |
| own chapter</a>). For Jython objects, it will delegate to |
| <code class="inline-code">freemarker.ext.jython.JythonWrapper</code>. For all |
| other objects, it will invoke <code class="inline-code">BeansWrapper.wrap</code> |
| (the super class's method), which will expose the JavaBean |
| properties of the objects as hash items (like |
| <code class="inline-code">myObj.foo</code> in FTL will call |
| <code class="inline-code">getFoo()</code> behind the scenes), and will also expose |
| the public methods (JavaBean actions) of the object (like |
| <code class="inline-code">myObj.bar(1, 2)</code> in FTL will call a method). (For |
| more information about BeansWrapper, <a href="pgui_misc_beanwrapper.html">see its own section</a>.)</p> |
| |
| <p>Some further details that's worth mentioning about |
| <code class="inline-code">DefaultObjectWrapper</code>:</p> |
| |
| <ul> |
| <li> |
| <p>You shouldn't use its constructor usually, instead create |
| it using a <code class="inline-code">DefaultObjectWrapperBuilder</code>. This |
| allows FreeMarker to use singletons.</p> |
| </li> |
| |
| <li> |
| <p><a name="topic.defaultObjectWrapperIcI"></a><code class="inline-code">DefaultObjectWrapper</code> has an |
| <code class="inline-code">incompatibleImprovements</code> property, that's |
| highly recommended to set it to a high value (see the <a href="https://freemarker.apache.org/docs/api/freemarker/template/DefaultObjectWrapper.html#DefaultObjectWrapper-freemarker.template.Version-">API |
| documentation</a> for the effects). How to set it:</p> |
| |
| <ul> |
| <li> |
| <p>If you have set the |
| <code class="inline-code">incompatible_improvements</code> setting |
| <em>of the <code class="inline-code">Configuration</code></em> |
| to 2.3.22 or higher, and you didn't set the |
| <code class="inline-code">object_wrapper</code> setting (so it had |
| remained on its default value), then you have to do nothing, |
| as it already uses a <code class="inline-code">DefaultObjectWrapper</code> |
| singleton with the equivalent |
| <code class="inline-code">incompatibleImprovements</code> property |
| value.</p> |
| </li> |
| |
| <li> |
| <p><a name="topic.setDefaultObjectWrapperIcIIndividually"></a>Otherwise you have to set the |
| <code class="inline-code">incompatibleImprovements</code> independently of |
| the <code class="inline-code">Configuration</code>. Depending on how you |
| create/set the <code class="inline-code">ObjectWrapper</code>, it can be |
| done like this:</p> |
| |
| <ul> |
| <li> |
| <p>If you are using the builder API:</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">... = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_27).build()</pre> </div> |
| |
| </li> |
| |
| <li> |
| <p>Or, if you are using the constructor:</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">... = new DefaultObjectWrapper(Configuration.VERSION_2_3_27)</pre> </div> |
| |
| </li> |
| |
| <li> |
| <p>Or, if you are using the |
| <code class="inline-code">object_wrapper</code> property |
| (<code class="inline-code">*.properties</code> file or |
| <code class="inline-code">java.util.Properties</code> object):</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">object_wrapper=DefaultObjectWrapper(2.3.27)</pre> </div> |
| |
| </li> |
| |
| <li> |
| <p>Or, if you are configuring the |
| <code class="inline-code">object_wrapper</code> through a |
| <code class="inline-code">FreemarkerServlet</code> with an |
| <code class="inline-code">init-param</code> in |
| <code class="inline-code">web.xml</code>:</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body"><init-param> |
| <param-name>object_wrapper</param-name> |
| <param-value>DefaultObjectWrapper(2.3.27)</param-value> |
| </init-param></pre> </div> |
| |
| </li> |
| </ul> |
| </li> |
| </ul> |
| </li> |
| |
| <li> |
| <p>In new or properly test-covered projects it's also |
| recommended to set the |
| <code class="inline-code">forceLegacyNonListCollections</code> property to |
| <code class="inline-code">false</code>. If you are using |
| <code class="inline-code">.properties</code> or |
| <code class="inline-code">FreemarkerServlet</code> init-params or such, that |
| will look like <code class="inline-code">DefaultObjectWrapper(2.3.22, |
| forceLegacyNonListCollections=false)</code>, while with the |
| Java API you call |
| <code class="inline-code">setForceLegacyNonListCollections(false)</code> on |
| the <code class="inline-code">DefaultObjectWrapperBuilder</code> object before |
| calling <code class="inline-code">build()</code>.</p> |
| </li> |
| |
| <li> |
| <p>The most common way of customizing |
| <code class="inline-code">DefaultObjectWrapper</code> is overriding its |
| <code class="inline-code">handleUnknownType</code> method.</p> |
| </li> |
| </ul> |
| |
| |
| |
| |
| |
| <h2 class="content-header header-section2" id="pgui_datamodel_customObjectWrappingExample">Custom object wrapping example</h2> |
| |
| |
| |
| |
| |
| |
| |
| |
| <p>Let's say you have an application-specific class like |
| this:</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">package com.example.myapp; |
| |
| public class Tupple<E1, E2> { |
| public Tupple(E1 e1, E2 e2) { ... } |
| public E1 getE1() { ... } |
| public E2 getE2() { ... } |
| }</pre> </div> |
| |
| |
| <p>You want templates to see this as a sequence of length 2, so |
| that you can do things like <code class="inline-code">someTupple[1]</code>, |
| <code class="inline-code"><#list someTupple |
| <em class="code-color">...</em>></code>, or |
| <code class="inline-code">someTupple?size</code>. For that you need to create a |
| <code class="inline-code">TemplateSequenceModel</code> implementation that adapts |
| a <code class="inline-code">Tupple</code> to the |
| <code class="inline-code">TempateSequenceMoldel</code> interface:</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">package com.example.myapp.freemarker; |
| |
| ... |
| |
| public class TuppleAdapter extends WrappingTemplateModel implements TemplateSequenceModel, |
| AdapterTemplateModel { |
| |
| private final Tupple<?, ?> tupple; |
| |
| public TuppleAdapter(Tupple<?, ?> tupple, ObjectWrapper ow) { |
| super(ow); // coming from WrappingTemplateModel |
| this.tupple = tupple; |
| } |
| |
| @Override // coming from TemplateSequenceModel |
| public int size() throws TemplateModelException { |
| return 2; |
| } |
| |
| @Override // coming from TemplateSequenceModel |
| public TemplateModel get(int index) throws TemplateModelException { |
| switch (index) { |
| case 0: return wrap(tupple.getE1()); |
| case 1: return wrap(tupple.getE2()); |
| default: return null; |
| } |
| } |
| |
| @Override // coming from AdapterTemplateModel |
| public Object getAdaptedObject(Class hint) { |
| return tupple; |
| } |
| |
| }</pre> </div> |
| |
| |
| <p>Regarding the classes and interfaces:</p> |
| |
| <ul> |
| <li> |
| <p><code class="inline-code">TemplateSequenceModel</code>: This is why the |
| template will see this as a sequence</p> |
| </li> |
| |
| <li> |
| <p><code class="inline-code">WrappingTemplateModel</code>: Just a |
| convenience class, used for <code class="inline-code">TemplateModel</code>-s |
| that do object wrapping themselves. That's normally only needed |
| for objects that contain other objects. See the |
| <code class="inline-code">wrap(<em class="code-color">...</em>)</code> calls |
| above.</p> |
| </li> |
| |
| <li> |
| <p><code class="inline-code">AdapterTemplateModel</code>: Indicates that |
| this template model adapts an already existing object to a |
| <code class="inline-code">TemplateModel</code> interface, thus unwrapping |
| should give back that original object.</p> |
| </li> |
| </ul> |
| |
| <p>Lastly, we tell FreeMarker to wrap <code class="inline-code">Tupple</code>-s |
| with the <code class="inline-code">TuppleAdapter</code> (alternatively, you could |
| wrap them manually before passing them to FreeMarker). For that, |
| first we create a custom object wrapper:</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">package com.example.myapp.freemarker; |
| |
| ... |
| |
| public class MyAppObjectWrapper extends DefaultObjectWrapper { |
| |
| public MyAppObjectWrapper(Version incompatibleImprovements) { |
| super(incompatibleImprovements); |
| } |
| |
| @Override |
| protected TemplateModel handleUnknownType(final Object obj) throws TemplateModelException { |
| if (obj instanceof Tupple) { |
| return new TuppleAdapter((Tupple<?, ?>) obj, this); |
| } |
| |
| return super.handleUnknownType(obj); |
| } |
| |
| }</pre> </div> |
| |
| |
| <p>and then where you configure FreeMarker (<a href="pgui_config.html">about configuring, see here...</a>) we plug |
| our object wrapper in:</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">// Where you initialize the cfg *singleton* (happens just once in the application life-cycle): |
| cfg = new Configuration(Configuration.VERSION_2_3_27); |
| ... |
| cfg.setObjectWrapper(new MyAppObjectWrapper(cfg.getIncompatibleImprovements()));</pre> </div> |
| |
| |
| <p>or if you are configuring FreeMarker with |
| <code class="inline-code">java.util.Properties</code> instead (and let's say it's |
| also a <code class="inline-code">.properties</code> file):</p> |
| |
| |
| |
| <div class="code-block role-unspecified"> |
| <pre class="code-block-body">object_wrapper=com.example.myapp.freemarker.MyAppObjectWrapper(2.3.27)</pre> </div> |
| |
| <div class="bottom-pagers-wrapper"><div class="pagers bottom"><a class="paging-arrow previous" href="pgui_datamodel_node.html"><span>Previous</span></a><a class="paging-arrow next" href="pgui_config.html"><span>Next</span></a></div></div></div></div> </div> |
| </div> |
| <div class="site-footer"><div class="site-width"><div class="footer-top"><div class="col-left sitemap"><div class="column"><h3 class="column-header">Overview</h3><ul><li><a href="https://freemarker.apache.org/">What is FreeMarker?</a></li><li><a href="https://freemarker.apache.org/freemarkerdownload.html">Download</a></li><li><a href="app_versions.html">Version history</a></li><li><a href="app_faq.html">FAQ</a></li><li><a itemprop="license" href="app_license.html">License</a></li><li><a href="https://privacy.apache.org/policies/privacy-policy-public.html">Privacy policy</a></li></ul></div><div class="column"><h3 class="column-header">Often used / Reference</h3><ul><li><a href="https://try.freemarker.apache.org/">Try template online</a></li><li><a href="dgui_template_exp.html#exp_cheatsheet">Expressions cheatsheet</a></li><li><a href="ref_directive_alphaidx.html">#directives</a></li><li><a href="ref_builtins_alphaidx.html">?built_ins</a></li><li><a href="ref_specvar.html">.special_vars</a></li><li><a href="api/freemarker/core/Configurable.html#setSetting-java.lang.String-java.lang.String-">Configuration settings</a></li></ul></div><div class="column"><h3 class="column-header">Community</h3><ul><li><a href="https://github.com/apache/freemarker">Github project page</a></li><li><a href="https://issues.apache.org/jira/projects/FREEMARKER">Report a bug</a></li><li><a href="https://freemarker.apache.org/report-security-vulnerabilities.html">Report security vulnerability</a></li><li><a href="https://stackoverflow.com/questions/ask?tags=freemarker">Get help on StackOverflow</a></li><li><a href="https://twitter.com/freemarker">Announcements on Twitter</a></li><li><a href="https://freemarker.apache.org/mailing-lists.html">Discuss on mailing lists</a></li></ul></div></div><div class="col-right"><ul class="social-icons"><li><a class="github" href="https://github.com/apache/freemarker">Github</a></li><li><a class="twitter" href="https://twitter.com/freemarker">Twitter</a></li><li><a class="stack-overflow" href="https://stackoverflow.com/questions/ask?tags=freemarker">Stack Overflow</a></li></ul><a class="xxe" href="http://www.xmlmind.com/xmleditor/" rel="nofollow" title="Edited with XMLMind XML Editor"><span>Edited with XMLMind XML Editor</span></a></div></div><div class="footer-bottom"> <p class="last-generated"> |
| Last generated: |
| <time itemprop="dateModified" datetime="2024-02-12T20:34:04Z" title="Monday, February 12, 2024 at 8:34:04 PM Greenwich Mean Time">2024-02-12 20:34:04 GMT</time>, for Freemarker 2.3.32 </p> |
| <p class="copyright"> |
| © <span itemprop="copyrightYear">1999</span>–2024 |
| <a itemtype="http://schema.org/Organization" itemprop="copyrightHolder" href="https://apache.org/">The Apache Software Foundation</a>. Apache FreeMarker, FreeMarker, Apache Incubator, Apache, the Apache FreeMarker logo are trademarks of The Apache Software Foundation. All other marks mentioned may be trademarks or registered trademarks of their respective owners. </p> |
| </div></div></div></body> |
| </html> |