Skip to content
Skip to breadcrumbs
Skip to header menu
Skip to action menu
Skip to quick search
Quick Search
Browse
Pages
Blog
Labels
Attachments
Mail
Advanced
What’s New
Space Directory
Feed Builder
Keyboard Shortcuts
Confluence Gadgets
Log In
Dashboard
Groovy
Copy Page
You are not logged in. Any changes you make will be marked as
anonymous
. You may want to
Log In
if you already have an account. You can also
Sign Up
for a new account.
This page is being edited by
.
Paragraph
Paragraph
Heading 1
Heading 2
Heading 3
Heading 4
Heading 5
Heading 6
Preformatted
Quote
Bold
Italic
Underline
More colours
Strikethrough
Subscript
Superscript
Monospace
Clear Formatting
Bullet list
Numbered list
Outdent
Indent
Align left
Align center
Align right
Link
Table
Insert
Insert Content
Image
Link
Attachment
Symbol
Emoticon
Wiki Markup
Horizontal rule
tinymce.confluence.insert_menu.macro_desc
Info
JIRA Issue
Status
Gallery
Tasklist
Table of Contents
Other Macros
Page Layout
No Layout
Two column (simple)
Two column (simple, left sidebar)
Two column (simple, right sidebar)
Three column (simple)
Two column
Two column (left sidebar)
Two column (right sidebar)
Three column
Three column (left and right sidebars)
Undo
Redo
Find/Replace
Keyboard Shortcuts Help
<p><table class="wysiwyg-macro" data-macro-name="excerpt" data-macro-parameters="atlassian-macro-output-type=BLOCK|hidden=true" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2V4Y2VycHQ6aGlkZGVuPXRydWV8YXRsYXNzaWFuLW1hY3JvLW91dHB1dC10eXBlPUJMT0NLfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>MetaBuilder is a builder that builds builders.</p></td></tr></table></p> <h2>MetaBuilder</h2> <p>As if there weren't enough options already for constructing your own builders in Groovy, along comes another: MetaBuilder. Quite literally, MetaBuilder is a builder that builds builders. Through some simple examples, this article will show you how you can put MetaBuilder to work for you in just three easy steps. To follow along, simply visit <a href="http://sourceforge.net/projects/groovytools/files/groovytools-builder/">SourceForge</a> to get the MetaBuilder distribution and include <code><strong>groovytools-builder-x.x.x.jar</strong></code> in your classpath.</p> <p>Oh, and you'll also need Groovy 1.5 and Java 1.5, too <img class="emoticon emoticon-smile" data-emoticon-name="smile" border="0" src="/s/en_GB/3278/15/_/images/icons/emoticons/smile.png" alt="(smile)" title="(smile)" /></p> <h2>MetaBuilder in Three Easy Steps</h2> <ol> <li>Create an instance of MetaBuilder</li> <li>Define your domain specific language (DSL)</li> <li>Build your objects</li> </ol> <h3>Create an instance of MetaBuilder</h3> <p>There's almost nothing to this step but just to call a constructor:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Step 1" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPVN0ZXAgMX0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>import groovytools.builder.* MetaBuilder mb = new MetaBuilder(getClass().getClassLoader()) </pre></td></tr></table> <table class="wysiwyg-macro" data-macro-name="tip" data-macro-parameters="title=Tip" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3RpcDp0aXRsZT1UaXB9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>You don't have to pass MetaBuilder a class loader, but in some cases, especially in scripts that declare new classes such as this one, it's necessary. This can be done by setting the <code>classLoader</code> property or by passing the class loader in MetaBuilder's constructor, as shown above.</p></td></tr></table> <h3>Define Your Domain Specific Langauge (DSL)</h3> <p>MetaBuilder provides a DSL, implemented as a Groovy builder, for defining your own builders. If you are already familiar with builders in Groovy, it takes just one simple example to get you started. Continuing where we left off above, let's start defining a customer class and customer builder:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Step 2" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPVN0ZXAgMn0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>class Customer { def name def dateOfBirth def ssn def phone } mb.define { customer(factory: Customer) { properties { name() dateOfBirth() ssn() } } } </pre></td></tr></table> <p>In the previous snippet, <code>define</code> is used to tell MetaBuilder that we are going to create some new definitions, or schema, for the objects that our new builder can create. MetaBuilder keeps track of our definitions and it's even possible to reuse and extend these definitions, as we'll see later.</p> <p><code>customer</code> is the name of the schema and the <code>factory</code> attribute tells MetaBuilder what object to create whenever a customer is to be built. <code>properties</code> contains a list of property names. </p> <table class="wysiwyg-macro" data-macro-name="tip" data-macro-parameters="title=Tip" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3RpcDp0aXRsZT1UaXB9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>MetaBuilder will throw an excception if a build script attempts to use unspecified or mispelled properties. So, for example, even though <code>phone</code> is a member of <code>Customer</code>, MetaBuilder won't allow you to use it unless you add it to your schema.</p></td></tr></table> <h3>Build Your Objects</h3> <p>Building objects is now just a matter of telling MetaBuilder that is what you want to do:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Step 3" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPVN0ZXAgM30&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>def aCustomer = mb.build { customer { name = 'J. Doe' dateOfBirth = '1/1/1900' ssn = '555-55-5555' } } // this is equivalent aCustomer = mb.build { customer ( name: 'J. Doe', dateOfBirth: '1/1/1900', ssn: '555-55-5555') } // you can even mix up the styles: aCustomer = mb.build { customer ( name: 'J. Doe', dateOfBirth: '1/1/1900') { ssn = '555-55-5555' } } </pre></td></tr></table> <p>Great! If you've been following along in your own IDE, you hopefully now have the basics down and are ready to take a look at some advanced techniques.</p> <table class="wysiwyg-macro" data-macro-name="tip" data-macro-parameters="title=Tip" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3RpcDp0aXRsZT1UaXB9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p><code>MetaBuilder.build()</code> returns the last object constructed. If your build script creates multiple objects, use the <code>buildList</code> method instead to return all of them.</p></td></tr></table> <h2>Catching Errors</h2> <p>What would happen if you used a property that was not in the schema or mistyped a legitimate property name? For example:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Catching Errors" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPUNhdGNoaW5nIEVycm9yc30&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>aCustomer = mb.build { customer { name = 'J. Doe' dob = '1/1/1900' // should have been 'dataOfBirth' ssn = '555-55-5555' phone = '1-555-555-5555' // not allowed } } </pre></td></tr></table> <p>If you executed this, MetaBuilder will throw the following exception:</p> <p><code><strong>groovytools.builder.PropertyException: Property 'dob': property unkown</strong></code></p> <p>Assuming you fix <code>dob</code> only and retry, MetaBuilder will throw another exception:</p> <p><code><strong>groovytools.builder.PropertyException: Property 'phone': property unkown</strong></code></p> <p>Despite the fact that <code>phone</code> is an actual property of your class, MetaBuilder only goes by what's in the schema. That kind of checking can protect private or sensitive properties. Read on to see how MetaBuilder can do even more with some additional information.</p> <h2>Controlling the Build</h2> <p>The purpose of this section is to go a bit deeper into MetaBuilder's capabilities.</p> <h3>More on <code>factory</code></h3> <p>Let's take another look at the <code>factory</code> attribute used earlier. By simply specifying the <code>factory</code> attribute in your schemas, you tell MetaBuilder how to build the right object every time. MetaBuilder was designed to accept as wide a variety of values as possible. For example, you can specify the <code>factory</code> attribute as any of the following:</p> <ul> <li>Class, i.e. <code>Customer</code></li> <li>String, i.e. <code>'Customer'</code></li> <li>instance of <code>groovy.util.Factory</code></li> <li>Closure</li> </ul> <p>Feel free to consult <a href="http://groovytools.sourceforge.net/builder/doc/api/groovytools/builder/MetaBuilder.html#metaschema">MetaBuilder Meta-Schema</a> for all the gory details on each of the attribute values MetaBuilder accepts.</p> <p>This next example demonstrates how one might use closure to create <code>Customer</code> objects:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Specifying a Factory using a Closure" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPVNwZWNpZnlpbmcgYSBGYWN0b3J5IHVzaW5nIGEgQ2xvc3VyZX0&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>mb.define { customer2(factory: { new Customer() } )) { properties { name() dateOfBirth() ssn() } } } def aCustomer2 = mb.build { customer2 { name = 'J. Doe' dateOfBirth = '1/1/1900' ssn = '555-55-5555' } } </pre></td></tr></table> <h3>Property Attributes</h3> <p>MetaBuilder supports a number of useful attributes on properties. These include the following:</p> <ul> <li><code><strong>def</strong></code>: the default value or a Closure to produce the default value, as needed</li> <li><code><strong>req</strong></code>: if true, a value must be specfied (it could be null though)</li> <li><code><strong>property</strong></code>: an alternative property name or Closure to use to set the value</li> <li><code><strong>min</strong></code>: the minimum length for a property value</li> <li><code><strong>max</strong></code>: the maximum length for a property value</li> <li><code><strong>check</strong></code>: causes an exception if the value fails the following test: <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>switch(value) { case check: return true } return false </pre></td></tr></table> <table class="wysiwyg-macro" data-macro-name="tip" data-macro-parameters="title=Tip" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e3RpcDp0aXRsZT1UaXB9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="RICH_TEXT"><tr><td class="wysiwyg-macro-body"><p>The <code><strong>check</strong></code> attribute accepts the following types of objects which makes enforcing constraints easy:</p> <ul> <li><strong>Closures</strong></li> <li><strong>Patterns</strong></li> <li><strong>Classes</strong></li> <li><strong>Numbers</strong></li> <li><strong>Strings</strong></li> <li><strong>Ranges</strong></li> <li><strong>Collections</strong></li> </ul> </td></tr></table> Here's an example showing combinations of some of the above: <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=MetaBuilder Property Attributes in Action" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPU1ldGFCdWlsZGVyIFByb3BlcnR5IEF0dHJpYnV0ZXMgaW4gQWN0aW9ufQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>mb.define { customer3(factory: Customer) { properties { name(req: true, min: 1) dob(property: 'dateOfBirth') ssn(check: ~/\d{3}-\d{2}-\d{4}/) } } def aCustomer3 = mb.build { customer4 { name = 'J. Doe' dob = '1/1/1900' ssn = '555-55-5555' } } </pre></td></tr></table></li> </ul> <h3>Reusing and Extending Schema: the <code>schema</code> Attribute</h3> <p>Use the <code>schema</code> attribute to tell MetaBuilder that you want to reuse a schema. In the next example, we'll create a new <code>Phone</code> class and update our schema to use it:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Adding a Phone Class and Schema" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPUFkZGluZyBhIFBob25lIENsYXNzIGFuZCBTY2hlbWF9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>class Phone { def type def number } mb.define { phone (factory: Phone) { properties { type(check: ['home','cell','work'], def: 'home') number(req: true, check: ~/\d{3}-\d{3}-\d{4}/) } } } </pre></td></tr></table> <p>Now, let's make <code>phone</code> a required property on our <code>customer</code> schema:</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Using the Phone Schema" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPVVzaW5nIHRoZSBQaG9uZSBTY2hlbWF9&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>mb.define { customer4(factory: Customer) { properties { name(req: true, min: 1) dob(property: 'dateOfBirth') ssn(check: ~/\d{3}-\d{2}-\d{4}/) phone(schema: 'phone', req: true) } } } def aCustomer4 = mb.build { customer4 { name = 'J. Doe' dob = '1/1/1900' ssn = '555-55-5555' phone { type = 'home' number = '123-456-7890' } } } </pre></td></tr></table> <h3>Collections</h3> <p>So far, we've only looked at examples of properties, but MetaBuilder also supports collections. You define collections like you define properties, just provide a list of them and set attributes as needed.</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Defining Schema with Indexed and Non-Indexed Collections" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPURlZmluaW5nIFNjaGVtYSB3aXRoIEluZGV4ZWQgYW5kIE5vbi1JbmRleGVkIENvbGxlY3Rpb25zfQ&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>class Customer2 extends Customer { def phoneList = [] def addresses = [:] } mb.define { customer5(factory: Customer2) { properties { name(req: true, min: 1) dob(property: 'dateOfBirth') ssn(check: ~/\d{3}-\d{2}-\d{4}/) phone(schema: 'phone', req: true) } collections { phoneList(min: 1) { phone(schema: phone) } addresses (key: 'type', min: 1, max:2) { address() { properties { type(check: ['billto', 'shipto'], def: 'billto') street() city() state() zip() } } } } } } </pre></td></tr></table> <p>The above definition extends Customer and adds a list of phone numbers and a map of addresses. Like properties, collections are mapped to properties of an object by the name.</p> <p>Note how <code>address</code> is defined directly within the collection. Nesting definitions can make the definitiona bit more brief, but comes at the risk of creating definitions that aren't as easily reused.</p> <p>Another thing to note is the use of the <code>key</code> attribute in the <code>addresses</code> collection. Presence of the <code>key</code> attribute tells MetaBuilder that the parent-child relationship is indexed. In the following example, you can see that <code>customer5</code> has both a list of <code>phone</code> and map of <code>addresses</code> using the <code>address's</code> <code>type</code><br /> and the key.</p> <table class="wysiwyg-macro" data-macro-name="code" data-macro-parameters="borderStyle=dashed|title=Building Objects with Indexed and Non-Indexed Collections" style="background-image: url(/plugins/servlet/confluence/placeholder/macro-heading?definition=e2NvZGU6Ym9yZGVyU3R5bGU9ZGFzaGVkfHRpdGxlPUJ1aWxkaW5nIE9iamVjdHMgd2l0aCBJbmRleGVkIGFuZCBOb24tSW5kZXhlZCBDb2xsZWN0aW9uc30&locale=en_GB&version=2); background-repeat: no-repeat;" data-macro-body-type="PLAIN_TEXT"><tr><td class="wysiwyg-macro-body"><pre>def aCustomer5 = mb.build { customer5 { name = 'J. Doe' dob = '1/1/1900' ssn = '555-55-5555' phone { type = 'home' number = '123-456-7890' } phoneList { phone(type: 'work', number: '111-222-3333') phone(type: 'cell', number: '444-555-6666') } address { type = 'billto' street = '1234 Some St.' city = 'Some City' zip = '12345' } address { type = 'shipto' street = '1234 Some Other St.' city = 'Some Other City' zip = '12345' } } } </pre></td></tr></table> <p>Collections support a number of useful properties:</p> <ul> <li><code><strong>collection</strong></code>: specifies an alternate name for the collection or a Closure used to get the collection from the parent.</li> <li><code><strong>key</strong></code>: specifies a property name or a Closure used to get the key from a child. Also indicates the collection is a map.</li> <li><code><strong>add</strong></code>: specifies a method name or Closure used to add the child directly to the parent, supercedes <code>collection</code>.</li> <li><code><strong>min</strong></code>: the minimum size of the collection</li> <li><code><strong>max</strong></code>: the maximum size of the collection</li> <li><code><strong>size</strong></code>: alternate method or Closure that may be used to get the collection size.</li> </ul> <h2>Where to Go From Here</h2> <p>This whirlwind tour of MetaBuilder really only just scratched the surface of its features and capabilities. If you get stuck, be sure to check out the <a href="http://groovytools.sourceforge.net/builder/doc/api/groovytools/builder/MetaBuilder.html#metaschema">MetaBuilder Meta-Schema</a>, which describes the entire MetaBuilder feature set.</p> <p>Also included in the release are a number of tests and examples that are also useful to look at.</p> <h2>Mailing List</h2> <ul> <li><a href="mailto:user@groovy.codehaus.com">user@groovy.codehaus.com</a></li> </ul> <h2>Developer(s)</h2> <ul> <li><a class="confluence-link confluence-userlink" data-username="didge" href="/display/~didge" data-linked-resource-id="110493698" data-linked-resource-type="userinfo" username="didge" data-linked-resource-default-alias="Didge" data-base-url="http://docs.codehaus.org">Didge</a></li> </ul>
Please type the word appearing in the picture.
Attachments
Labels
Location
Watch this page
< Edit
Preview >
Loading…
Save
Cancel
Next hint
search
attachments
weblink
advanced