blob: 2a3cb0f493961fb84170b7f0ccb741b03dfd92fc [file] [log] [blame]
<!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 - 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="FreeMarker Manual">
<meta property="og:title" content="Object wrappers">
<meta property="og:locale" content="en_US">
<meta property="og:url" content="http://example.com/pgui_datamodel_objectWrapper.html">
<link rel="canonical" href="http://example.com/pgui_datamodel_objectWrapper.html">
<link rel="icon" href="favicon.png" type="image/png">
<link rel="stylesheet" type="text/css" href="docgen-resources/docgen.min.css?1594338517553">
</head>
<body itemscope itemtype="https://schema.org/Code">
<meta itemprop="url" content="http://example.com/">
<meta itemprop="name" content="FreeMarker Manual">
<!--[if lte IE 9]>
<div style="background-color: #C00; color: #fff; padding: 12px 24px;">Please use a modern browser to view this website.</div>
<![endif]--><div class="header-top-bg"><div class="site-width header-top"><a class="logo" href="http://example.com" role="banner"> <img itemprop="image" src="logo.png" alt="My Logo">
</a></div></div><div class="header-bottom-bg"><div class="site-width search-row"><a href="index.html" class="navigation-header">FreeMarker Manual</a><div class="navigation-header"></div></div><div class="site-width breadcrumb-row"><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">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&#39;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 class="bookmarks" title="Bookmarks"><span class="sr-only">Bookmarks:</span><ul class="bookmark-list"><li><a href="alphaidx.html">Index</a></li><li><a href="gloss.html">Glossary</a></li><li><a href="ref.html">Reference</a></li><li><a href="app_faq.html">FAQ</a></li><li><a href="preface.html#test_target">Bőregér</a></li></ul></div></div></div> <div class="main-content site-width">
<div class="content-wrapper no-toc">
<div id="table-of-contents-wrapper" class="col-left">
</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><p>When you add something to a container, it may receive any java
object as a parameter, not necessarily a
<code class="inline-code">TemplateModel</code>, as you could see in the FreeMarker
API. This is because the container implementation can silently replace
that object with the appropriate <code class="inline-code">TemplateModel</code>
object. For example if you add a <code class="inline-code">String</code> to the
container, perhaps it will be replaced with a
<code class="inline-code">SimpleScalar</code> instance which stores the same
text.</p><p>As for when the replacement occurs, it&#39;s the business of the
container in question (i.e. the business of the class that implements
the container interface), but it must happen at the latest when you
get the subvariable, as the getter methods (according to the
interfaces) return <code class="inline-code">TemplateModel</code>, not
<code class="inline-code">Object</code>. For example, <code class="inline-code">SimpleHash</code>,
<code class="inline-code">SimpleSequence</code> and
<code class="inline-code">SimpleCollection</code> use the laziest strategy; they
replace a non-<code class="inline-code">TemplateModel</code> subvariable with an
appropriate <code class="inline-code">TemplateModel</code> object when you get the
subvariable for the first time.</p><p>As for what java objects can be replaced, and with what
<code class="inline-code">TemplateModel</code> implementations, it is either handled
by the container implementation itself, or it delegates this to an
<code class="inline-code">ObjectWrapper</code> instance.
<code class="inline-code">ObjectWrapper</code> is an interface that specifies one
method: <code class="inline-code">TemplateModel wrap(java.lang.Object obj)</code>.
You pass in an <code class="inline-code">Object</code>, and it returns the
corresponding <code class="inline-code">TemplateModel</code> object, or throws a
<code class="inline-code">TemplateModelException</code> if this is not possible. The
replacement rules are coded into the <code class="inline-code">ObjectWrapper</code>
implementation.</p><p>The most important <code class="inline-code">ObjectWrapper</code>
implementations that the FreeMarker core provides are:</p><ul>
<li>
<p><code class="inline-code">ObjectWrapper.DEFAULT_WRAPPER</code>: It
replaces <code class="inline-code">String</code> with
<code class="inline-code">SimpleScalar</code>, <code class="inline-code">Number</code> with
<code class="inline-code">SimpleNumber</code>, <code class="inline-code">List</code> and array
with <code class="inline-code">SimpleSequence</code>, <code class="inline-code">Map</code>
with <code class="inline-code">SimpleHash</code>, <code class="inline-code">Boolean</code>
with <code class="inline-code">TemplateBooleanModel.TRUE</code> or
<code class="inline-code">TemplateBooleanModel.FALSE</code>, W3C DOM nodes with
<code class="inline-code">freemarker.ext.dom.NodeModel</code>. For Jython
objects, this wrapper will invoke
<code class="inline-code">freemarker.ext.jython.JythonWrapper</code>. For all
other objects, it will invoke <a href="pgui_misc_beanwrapper.html"><code>BEANS_WRAPPER</code></a>.</p>
</li>
<li>
<p><code class="inline-code">ObjectWrapper.BEANS_WRAPPER</code>: It can
expose java Bean properties and other members of arbitrary objects
using Java reflection. At least in FreeMarker 2.3, it is a
<code class="inline-code">freemarker.ext.beans.BeansWrapper</code> instance;
there is a separated <a href="pgui_misc_beanwrapper.html">chapter
about it</a>.</p>
</li>
</ul><p>For a concrete example, let&#39;s see how the
<code class="inline-code">Simple<em class="code-color">Xxx</em></code> classes work.
<code class="inline-code">SimpleHash</code>, <code class="inline-code">SimpleSequence</code> and
<code class="inline-code">SimpleCollection</code> use
<code class="inline-code">DEFAULT_WRAPPER</code> to wrap the subvariables (unless
you pass in an alternative wrapper in their constructor). So this
example demonstrates <code class="inline-code">DEFAULT_WRAPPER</code> in
action:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">Map map = new HashMap();
map.put(&quot;anotherString&quot;, &quot;blah&quot;);
map.put(&quot;anotherNumber&quot;, new Double(3.14));
List list = new ArrayList();
list.add(&quot;red&quot;);
list.add(&quot;green&quot;);
list.add(&quot;blue&quot;);
SimpleHash root = new SimpleHash(); // will use the default wrapper
root.put(&quot;theString&quot;, &quot;wombat&quot;);
root.put(&quot;theNumber&quot;, new Integer(8));
root.put(&quot;theMap&quot;, map);
root.put(&quot;theList&quot;, list);</pre></div><p>Assuming that root is the data-model root, the resulting
data-model is:</p>
<div class="code-wrapper"><pre class="code-block code-data-model">(root)
|
+- theString = &quot;wombat&quot;
|
+- theNumber = 8
|
+- theMap
| |
| +- anotherString = &quot;blah&quot;
| |
| +- anotherNumber = 3.14
|
+- theList
|
+- (1st) = &quot;red&quot;
|
+- (2nd) = &quot;green&quot;
|
+- (3rd) = &quot;blue&quot;</pre></div><p>Note that the <code class="inline-code">Object</code>-s inside
<code class="inline-code">theMap</code> and <code class="inline-code">theList</code> are
accessible as subvariables too. This is because when you, say, try to
access <code class="inline-code">theMap.anotherString</code>, then the
<code class="inline-code">SimpleHash</code> (which is used as root hash here) will
silently replace the <code class="inline-code">Map</code>
(<code class="inline-code">theMap</code>) with a <code class="inline-code">SimpleHash</code>
instance that uses the same wrapper as the root hash, so when you try
to access the <code class="inline-code">anotherString</code> subvariable of it, it
will replace that with a <code class="inline-code">SimpleScalar</code>.</p><p>If you drop an ``arbitrary&#39;&#39; object into the data-model,
<code class="inline-code">DEFAULT_WRAPPER</code> will invoke
<code class="inline-code">BEANS_WRAPPER</code> to wrap the object:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">SimpleHash root = new SimpleHash();
// expose a &quot;simple&quot; java objects:
root.put(&quot;theString&quot;, &quot;wombat&quot;);
// expose an &quot;arbitrary&quot; java objects:
root.put(&quot;theObject&quot;, new TestObject(&quot;green mouse&quot;, 1200));</pre></div><p>Assuming this is <code class="inline-code">TestObject</code>:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">public class TestObject {
private String name;
private int price;
public TestObject(String name, int price) {
this.name = name;
this.price = price;
}
// JavaBean properties
// Note that public fields are not visible directly;
// you must write a getter method for them.
public String getName() {return name;}
public int getPrice() {return price;}
// A method
public double sin(double x) {
return Math.sin(x);
}
}</pre></div><p>The data-model will be:</p>
<div class="code-wrapper"><pre class="code-block code-data-model">(root)
|
+- theString = &quot;wombat&quot;
|
+- theObject
|
+- name = &quot;green mouse&quot;
|
+- price = 1200
|
+- number sin(number)</pre></div><p>So we can merge it with this template:</p>
<div class="code-wrapper"><pre class="code-block code-template">${theObject.name}
${theObject.price}
${theObject.sin(123)}</pre></div><p>Which will output:</p>
<div class="code-wrapper"><pre class="code-block code-output">green mouse
1200
-0,45990349068959124</pre></div><p>You have seen in earlier examples of this manual that we have
used <code class="inline-code">java.util.HashMap</code> as root hash, and not
<code class="inline-code">SimpleHash</code> or other FreeMarker specific class. It
works because
<code class="inline-code">Template.process(<em class="code-color">...</em>)</code>
automatically wraps the object you give as its data-model argument. It
uses the object wrapper dictated by the
<code class="inline-code">Configuration</code> level <a href="pgui_config_settings.html">setting</a>,
<code class="inline-code">object_wrapper</code> (unless you explicitly specify an
<code class="inline-code">ObjectWrapper</code> as its parameter). Thus, in simple
FreeMarker application you need not know about
<code class="inline-code">TemplateModel</code>-s at all. Note that the root need not
be a <code class="inline-code">java.util.Map</code>. It can be anything that is
wrapped so that it implements the <code class="inline-code">TemplateHashModel</code>
interface.</p><p>The factory default value of the
<code class="inline-code">object_wrapper</code> setting is
<code class="inline-code">ObjectWrapper.DEFAULT_WRAPPER</code>. If you want to
change it to, say, <code class="inline-code">ObjectWrapper.BEANS_WRAPPER</code>, you
can configure the FreeMarker engine (before starting to use it from
other threads) like this:</p>
<div class="code-wrapper"><pre class="code-block code-unspecified">cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);</pre></div><p>Note that you can set any object here that implements interface
<code class="inline-code">ObjectWrapper</code>, so you can set your custom
implementation as well.</p><p>For <code class="inline-code">TemplateModel</code> implementations that wrap
basic Java container types, as <code class="inline-code">java.util.Map</code>-s and
<code class="inline-code">java.util.List</code>-s, the convention is that they use
the same object wrapper to wrap their subvariables as their parent
container does. Technically correctly said, they are instantiated by
their parent container (so it has full control over the creation of
them), and the parent container create them so they will use the same
object wrapper as the parent itself. Thus, if
<code class="inline-code">BEANS_WRAPPER</code> is used for the wrapping of the root
hash, it will be used for the wrapping of the subvariables (and the
subvariables of the subvariables, etc.) as well. This is exactly the
same phenomenon as you have seen with
<code class="inline-code">theMap.anotherString</code> earlier.</p><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-bottom"> <p class="last-generated">
Last generated:
<time itemprop="dateModified" datetime="2020-07-09T23:48:37Z" title="Thursday, July 9, 2020 11:48:37 PM GMT">2020-07-09 23:48:37 GMT</time> </p>
<p class="copyright">
© <span itemprop="copyrightYear">1999</span>–2020
<a itemtype="http://schema.org/Organization" itemprop="copyrightHolder" href="https://apache.org/">The Apache Software Foundation</a> </p>
</div></div></div></body>
</html>