<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>My Diversions &#187; Tapestry</title>
	<atom:link href="http://www.kablambda.org/blog/category/computer-science/java/tapestry/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kablambda.org/blog</link>
	<description>Notes on things I'm thinking and doing</description>
	<lastBuildDate>Tue, 20 Oct 2009 11:31:55 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>CAL and Tapestry 5, Part 2: Algebraic Types and Forms</title>
		<link>http://www.kablambda.org/blog/2007/10/03/cal-and-tapestry-5-part-2-algebraic-types-and-forms/</link>
		<comments>http://www.kablambda.org/blog/2007/10/03/cal-and-tapestry-5-part-2-algebraic-types-and-forms/#comments</comments>
		<pubDate>Wed, 03 Oct 2007 11:29:17 +0000</pubDate>
		<dc:creator>Tom Davies</dc:creator>
				<category><![CDATA[CAL and Open Quark]]></category>
		<category><![CDATA[Computer Science]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Tapestry]]></category>

		<guid isPermaLink="false">http://diversions.nfshost.com/blog/2007/10/03/cal-and-tapestry-5-part-2-algebraic-types-and-forms/</guid>
		<description><![CDATA[In my previous post I described how to use a CAL function as part of the implementation of a Java class.

This post looks at interfacing CAL to Tapestry 5 using the &#8216;Java Bean&#8217; conventions of getter and setter methods for the fields in an object.

Tapestry 5 provides a BeanEditForm component which simplifies providing CRUD operations [...]]]></description>
			<content:encoded><![CDATA[<p>In my previous post I described how to use a <a href="http://labs.businessobjects.com/cal/">CAL</a> function as part of the implementation of a Java class.</p>

<p>This post looks at interfacing CAL to <a href="http://tapestry.apache.org/tapestry5/">Tapestry 5</a> using the &#8216;Java Bean&#8217; conventions of getter and setter methods for the fields in an object.</p>

<p>Tapestry 5 provides a <a href="http://tapestry.formos.com/nightly/tapestry5/tapestry-core/guide/beaneditform.html">BeanEditForm</a> component which simplifies providing CRUD operations for Beans. This is described in the <a href="http://tapestry.apache.org/tapestry5/tutorial1/forms.html">second part of the Tapestry 5 tutorial</a>.</p>

<p>By creating a Java class which provides a Bean with fields equivalent to the constructor parameters of a  CAL algebraic data type we can use CAL to provide the data model for a web UI created with Tapestry.
<span id="more-19"></span>
A Tapestry application doesn&#8217;t need to have a compile-time dependency on the fields in a Bean which will be edited in a <code>BeanEditForm</code>. This means that we can simply declare an abstract class, associate it with a CAL data constructor and have immediate web-based CRUD for that type.</p>

<p>On the Java side, we make the following changes to the code described in <a href="http://tapestry.apache.org/tapestry5/tutorial1/forms.html">the tutorial</a>.</p>

<p>The <code>Address</code> class becomes:</p>

<pre><code>@CalBean(workspace="myworkspace.cws", module="TDavies.Address", constructorName = "Address")
public abstract class Address implements CalBeanBase {
}
</code></pre>

<p>The type referred to by the annotation looks like this:</p>

<pre><code>data Address = Address
    honorific :: String
    firstName :: String
    lastName :: String
    street1 :: String
    street2 :: (Maybe String)
    city :: String
    state :: String
    zip :: (Maybe Int)
    email :: String
    phone :: (Maybe String)
    fax :: (Maybe String)
deriving Inputable, Outputable, Show;
</code></pre>

<p>The type must be an instance of <code>Inputable</code> and <code>Outputable</code>.</p>

<p>The <code>CalBeanBase</code> interface provides the information about the Bean needed for copying between an instance of <code>Address</code> (or any other Bean constructed from a CAL type) and an <a href="http://resources.businessobjects.com/labs/cal/open_quark_javadoc/index.html">AlgebraicValue</a>:</p>

<pre><code>/**
 * This interface describes the structure of a Bean derived from a CAL type.
 * The information allows values to be transferred between bean instances and
 * CAL AlgebraicValue instances, using the BeanConverter class.
 */
public interface CalBeanBase {
    public static class FieldSpec {
        public String name;
        public BeanValueConverter converter;

        public FieldSpec(String name, BeanValueConverter converter) {
            this.name = name;
            this.converter = converter;
        }
    }
    public List&lt;FieldSpec&gt; getFieldSpecs();
    String getDataConstructorName();
    int getDataConstructorOrdinal();
}
</code></pre>

<p><code>BeanValueConverter</code> implementations copy between a CAL value and a field. At present the only non-trivial implementation is the <code>MAYBE</code> converter, which maps <code>Nothing</code> to <code>null</code>.</p>

<p>The <code>BeanConverter</code> class has these methods:</p>

<pre><code>/**
 * Copy values between CalBeanBase subclasses and CAL AlgebraicValue instances.
 */
public class BeanConverter {
    /**
     * Create a new AlgebraicValue which has been constructed using
     * the field values from bean.
     */
    public static AlgebraicValue toCal(CalBeanBase bean) {
        ...
    }

    /**
     * Set the field values in bean from the constructor parameters
     * in v.
     */
    public static void fromCal(CalBeanBase bean, AlgebraicValue v) {
        ...
    }
}
</code></pre>

<p>At runtime Javassist implements the CalBeanBase interface and adds the fields, getters and setters needed to make a bean. It does this by looking up the CAL type given in the annotation and introspecting it.</p>

<p>In addition, setters for fields which are not of type <code>Maybe a</code> are given a <code>@Validate("required")</code> annotation. <code>@Order</code> annotations are used to keep the fields in the same order as they appear in the CAL type. (The tutorial is out of date in this respect, and doesn&#8217;t mention the <code>@Order</code> annotation).</p>

<p>To exercise this technique we can make the <code>CreateAddress</code> class less trivial:</p>

<pre><code>       // make _address persistent, so we can see the results of processing it on submit
  @Persist
  private Address _address;
       ... getter and setter unchanged ...
       // CAL function -- the body will be replaced with a call to TDavies.Address.sendAddress
  @Cal
  public AlgebraicValue sendAddress(Object address) {return null;}

  public void onSubmit()
  {
      BeanConverter.fromCal(_address, sendAddress(BeanConverter.toCal(_address)));
  }
</code></pre>

<p>When we submit the form, the bean&#8217;s field values are converted to an AlgebraicValue and passed to the CAL function <code>sendAddress</code>. The value returned by the function is used to set the fields of the bean, which is then displayed.</p>

<p>The <code>sendAddress</code> function just converts some of the data to upper case:</p>

<pre><code>sendAddress :: Address -&gt; Address;
public sendAddress o =
    let
        address = o;
    in
        Address
            address.Address.honorific
            (toUpperCase address.Address.firstName)
            (toUpperCase address.Address.lastName)
            (toUpperCase address.Address.street1)
            (case address.Address.street2 of Nothing -&gt; Nothing; Just s -&gt; Just $ toUpperCase s;)
            (toUpperCase address.Address.city)
            (toUpperCase address.Address.state)
             address.Address.zip
             address.Address.email
             address.Address.phone
             address.Address.fax;
</code></pre>

<p>There are a number of enhancements which could be made to this scheme:</p>

<ul>
<li>Unlike the example, Java Enums are not supported as field values.</li>
<li>Only <code>required</code> validation can be specified, not <code>regex</code> or other types.</li>
<li>There is a problem allowing optional field values for non-string values &#8212; this may be a Tapestry limitation.</li>
<li>The <code>BeanConverter</code> methods should be on the <code>CalBeanBase</code> interface. Not only would the <code>onSubmit</code> implementation above be simpler, but the existing methods on the <code>CalBeanBase</code> interface could be removed, and implemented as private methods called by the toCal/fromCal implementations.</li>
</ul>

<p>To really tell whether these techniques are worthwhile, I need to write a non-trivial web app using them!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kablambda.org/blog/2007/10/03/cal-and-tapestry-5-part-2-algebraic-types-and-forms/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>CAL and the Tapestry 5 Tutorial</title>
		<link>http://www.kablambda.org/blog/2007/09/24/cal-and-the-tapestry-5-tutorial/</link>
		<comments>http://www.kablambda.org/blog/2007/09/24/cal-and-the-tapestry-5-tutorial/#comments</comments>
		<pubDate>Tue, 25 Sep 2007 04:13:32 +0000</pubDate>
		<dc:creator>Tom Davies</dc:creator>
				<category><![CDATA[CAL and Open Quark]]></category>
		<category><![CDATA[Computer Science]]></category>
		<category><![CDATA[Tapestry]]></category>

		<guid isPermaLink="false">http://diversions.nfshost.com/blog/2007/09/24/cal-and-the-tapestry-5-tutorial/</guid>
		<description><![CDATA[The technique described in my previous post can be used to create Tapestry 5 pages which call CAL functions. Tapestry also uses Javassist to enhance pages, so adding CAL integration requires that Tapestry is reconfigured to apply the CAL transformations in addition to its own &#8212; I wasn&#8217;t able to find a way to transparently [...]]]></description>
			<content:encoded><![CDATA[<p>The technique described in my previous post can be used to create <a href="http://tapestry.apache.org/tapestry5/">Tapestry 5</a> pages which call CAL functions. Tapestry also uses Javassist to enhance pages, so adding CAL integration requires that Tapestry is reconfigured to apply the CAL transformations in addition to its own &#8212; I wasn&#8217;t able to find a way to transparently modify the classes before Tapestry sees them.</p>

<p>I&#8217;ve modified the <a href="http://tapestry.apache.org/tapestry5/tutorial1/hilo.html">Hi/Lo Guessing Game</a> from the <a href="http://tapestry.apache.org/tapestry5/tutorial1/">Tapestry Tutorial</a> to use CAL implementations for some functions:</p>

<p><span id="more-17"></span></p>

<p>The Start page:</p>

<pre><code>@Cal(workspace = "myworkspace.cws", module = "TDavies.Start")
public abstract class Start {
    private final Iterator&lt;Integer&gt; _random = randomList();

    @Cal(outputPolicy=ITERATOR)
    protected abstract Iterator&lt;Integer&gt; randomList();

    @InjectPage
    private Guess _guess;

    Object onAction() {
        _guess.setup(_random.next().intValue());

        return _guess;
    }
}
</code></pre>

<p>Note that I have specified an output policy of <code>ITERATOR</code> for the <code>randomList</code> function. This alows us to retrieve lazily calculated random numbers as needed. (in fact, given a page&#8217;s lifecycle I think this value is just discarded between games)</p>

<p>The inter-page calls cannot easily be implemented in CAL, as far as I can see, so the setting up of the Guess page remains in Java. That&#8217;s a reasonable separation between the UI framework and application logic.</p>

<p>The CAL implementation for the above page is:</p>

<pre><code>randomList ::  [Int];
public randomList = map (\n -&gt; n + 1) $ randomBoundedInts initialSeed 10;
</code></pre>

<p>The Guess page:</p>

<pre><code>@Cal(workspace = "myworkspace.cws", module = "TDavies.Start")
public abstract class Guess {
    @Persist
    private int _target;

    void setup(int target)
    {
      _target = target;
    }

    ... the getters and setters are unchanged ...

    @Persist
    private String _message;

    @Cal(in="_target", out="_message")
    abstract String onActionFromLink(int guess);
}
</code></pre>

<p>with its CAL implementation:</p>

<pre><code>onActionFromLink :: Int -&gt; Int -&gt; (Maybe String, String);
public onActionFromLink guess target =
    let
        message word = (intToString guess) ++ " is too " ++ word ++ " please try again.";
    in
    (if guess == target then Just "GameOver" else Nothing,
     message (if guess &lt; target then "low" else "high"));
</code></pre>

<p>With more in and out values it would become hard to keep track of the identity of the parameters and return values, so a CAL Record might be a better fit.</p>

<p>This is a trivial application, but I like the ability to call CAL directly, rather than delegating to a wrapper class and packing/unpacking parameter structures on call and return.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kablambda.org/blog/2007/09/24/cal-and-the-tapestry-5-tutorial/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
