dooApp technical blog http://blog.dooapp.com Innovative software for green building posterous.com Thu, 30 Jun 2011 08:55:00 -0700 JavaFX 2.0, Forms and Bean Validation: FXForm2 is here! http://blog.dooapp.com/javafx-20-forms-and-bean-validation-fxform2-i http://blog.dooapp.com/javafx-20-forms-and-bean-validation-fxform2-i

...and we are back on our technical blog!

We started last year a project called FXForm providing automatic form generation in JavaFX 1.3. We have been using this library intensively in some of our applications. JavaFX 2.0 is now a public beta. It did not took too much time until we decided to migrate FXForm to this exciting new version of the JavaFX technology.

Actually, it was not really relevant to convert the old code to JavaFX 2.0 code. Most FXForm code was about binding and this part of the API has be heavily modified in JavaFX 2.0 to fit the constraints of the Java langage. So we decided to write it again from scratch -- and you know what? This was quicker than expected to achieve! The new JavaFX 2.0 API is intuitive and clear. By the way, some features of the Java ecosystem where much more easier to integrate such as the JSR 303 Bean Validation support.

So we are proud to announce FXForm2. Check our Get Started page to create your first form! Many features and default controls are still coming, but any help is welcome! Feel free to contact us.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Sat, 07 Aug 2010 06:59:45 -0700 Netbeans JavaFX Composer: Designing a CustomNode http://blog.dooapp.com/netbeans-javafx-composer-designing-a-customno http://blog.dooapp.com/netbeans-javafx-composer-designing-a-customno The JavaFX composer is a powerful tool to design interfaces but at first it's not really obvious how to split a complex interface into several pieces and how to handle custom nodes. In this article I'll present the approach that turned out to be the most efficient from my own experience after some months of work with this composer.

  • Create a new desktop design file called Main.fx that contains the common design parts of your application.
Main

  • Now let's say we have the following model object and its manager:
1
2
3
4
5
public class Doo {

    public-init var name: String;

}
1
2
3
4
5
public class DooManager {

    public var doos: Doo[];

}
  • Next step is to design the node responsible for rendering a Doo instance in the interface. Select New File > JavaFX Fragment Design File to create a DooNode.fx design file. This will create a design fragment that you can edit visually with the JavaFX composer. Let's switch to the "Source" view and add a field to this class that will contain the Doo instance edited/rendered in our node.
1
2
3
4
5
6
7
public class DooNode {

    [Generated Code]
    
    public-init var doo: Doo;

}
  • We can now design this node and bind it to the rendered object.
Doonode

  • Now the question is how to use this design file in the main interface? The composer generates a method called getDesignRootNodes() that gives you access to the parent nodes of the design file. Using this method to handle this node is not really handy since it's not defined in any interface and it won't be easy to switch from a renderer to another one. JavaFX define the concept of custom node through the CustomNode class. Why not using this class as parent class for our design file? Switch back to the source view and let DooNode extends CustomNode. Now just define the postinit method and insert the root nodes into the children of the custom node. Your class should look like this now:
1
2
3
4
5
6
7
8
9
10
11
public class DooNode extends CustomNode {

    [Generated Code]
    
    public-init var doo: Doo;

    postinit {
        insert getDesignRootNodes() into children;
    }

}
  • Our DesignNode class is now a classical CustomNode and can be manipulated in a transparent way in the main design file. Let's insert a flow layout into the Main design class and bind it's content to a sequence of DooNode associated to the doo's defined in the manager:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Main {

    [Generated Code]

    def manager = DooManager {
        doos: [
            Doo {
                name: "A doo"
            },
            Doo {
                name: "Another doo"
            },
            Doo {
                name: "Just a doo"
            }
            ]
        }

        postinit {
            insert Flow {
                content: bind for (doo in manager.doos) {
                    DooNode {
                        doo: doo
                    }
                }
                hgap: 6
                vgap: 6
            } into verticalBox.content;
        }

}

And that's it! If you run the application you should see something like this:
App

This approach provides a handy way to split the interface into several small pieces in a transparent way and to take advantage of the composer features.

Note: the Netbeans project is available in the CustomNodeDesign file attached to this post

--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.

CustomNodeDesign.zip Download this file

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Fri, 16 Jul 2010 02:04:00 -0700 Dude, Where is my form? http://blog.dooapp.com/dude-where-is-my-form http://blog.dooapp.com/dude-where-is-my-form

Building UI forms for editing model objects is a repetitive task. That's why we have started a small project called FXForm to provide automatic form generation for JavaFX objects. The goal of this library is to easily create customizable forms for any JavaFX object.

A small example might be more talkative. Let's imagine you have the following model class:

1
2
3
4
5
6
7
8
9
class EditedClass {

    public var name: String;
    public var age: Number;
    public var favoriteMovie: String;
    public var elem: TestEnum;
    public var birthday: DateTime;
    public var hungry: Boolean;
}

and you need a form to edit an instance of this class. FXForm allows you to generate this form with a few lines of code:

1
2
3
4
5
DefaultFormGenerator {
    edited: EditedClass{}
    reorder: ["name", "elem", "hungry"]
    excludes: ["favoriteMovie"]
}.generateForm()

and you will get something like this:

Fxform_demo

Checkout the Usage page of the project wiki for more information!

Dude_wheres_my_car

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Tue, 01 Jun 2010 05:02:00 -0700 IoC in JavaFX http://blog.dooapp.com/ioc-in-javafx http://blog.dooapp.com/ioc-in-javafx
loC frameworks are very useful when you develop modular applications. I've been working with Guice and Tapestry IoC (which is an independent module of the Tapestry project) in the past, but I had to abandon them when I switched to JavaFX since none of them seemed to be compatible with JavaFX.
The main issue was about the use of annotations. They are both heavily depending on annotations to define the injection mechanisms - and JavaFX unfortunately does not allow us to use annotations...
However, the Tapestry framework provides another way to access services by requesting them directly to a registry without using annotations. (I didn't find something similar in Guice, but it doesn't mean that there is no way to get Guice working with JavaFX).
To play with tapestry injection, the first thing you need is to define an interface describing your injected service, let's call it TestInterface:
1
2
3
4
5
public abstract class TestInterface {

    public abstract function test():Void;

}
(actually it's an abstract class, since JavaFX does not use interfaces, but it doesn't matter)
Next step is to define a concrete implementation of this interface that will be injected, for example TestInterfaceImpl:
1
2
3
4
5
6
7
8
public class TestInterfaceImpl extends TestInterface {

    override function test() {
        println("I'm the test impl");
    }

}

The core concept of Taspestry IoC framework are the modules. Basically a module define some bindings between interfaces and their implementations. Let's define a pure JavaFX module binding our injected service:
1
2
3
public function buildTestInterface(): TestInterface {
    TestInterfaceImpl {}
}
(Note that the concept of "autobuilding" will not be working, since it requires to define a "bind" method, which is a reserved keyword in JavaFX - not idea how to fix this for the moment)
We can now set up a registry with the defined module:
1
2
3
4
5
public var registry: Registry;
var builder: RegistryBuilder = new RegistryBuilder();
builder.add(TestModule.class);
registry = builder.build();
registry.performRegistryStartup();
Well, we are now ready to request the service corresponding to the TestInterface simply by asking it directly to the registry:
1
2
var impl: TestInterface = IoC.registry.getService(TestInterface.class);
impl.test();
And you should get the instance of TestInterface you defined in the corresponding module!
Finally, to get things a bit cleaner you can move all the registry stuff the a separate class:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public var registry: Registry;

public function init(): Void {
    println("starting up IoC registry");
    var builder: RegistryBuilder = new RegistryBuilder();
    builder.add(InjectionTestModule.class);
    registry = builder.build();
    registry.performRegistryStartup();
    FX.addShutdownAction(function(): Void {
        println("cleaning IoC registry");
        registry.cleanupThread();
        registry.shutdown();
    });
}

This way, IoC.registry will give you an access to your registry in your application. Just make a call at IoC.init() before using it. This is not as "beautiful" as annotations, but still much better than no IoC. Let me know if you get a more elegant solution!

Note: This article is not intended to be a tutorial for taspestry-ioc, you will find more documentation about it in this howto and on the official website. Moreover, there might be other IoC frameworks that work with JavaFX, just let me know if you are successfully using another one!
Note 2: Tapestry IoC is following a strict "fail-fast" policy and building the registry will fail if your module contains unrecognized methods. This is a bit problematic with JavaFX modules. Actually, all JavaFX classes implements the com.sun.javafx.runtime.FXObject interface. So a JavaFX class contains systematically a bunch of methods inherited from this interface, that tapestry does not recognize. This will lead to an error message like this when you try to register your JavaFX module into the registry: "java.lang.RuntimeException: Module class injectiontest.services.TestModule contains unrecognized public methods: public boolean com.sun.javafx.runtime.FXBase.getAsBoolean$(int,int),[...]". There is a corresponding jira entry. You can get the patch from this ticket to solve this issue for the moment.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Mon, 31 May 2010 06:30:53 -0700 JavaFX composer, dynamic layout and XScene http://blog.dooapp.com/javafx-composer-dynamic-layout-and-xscene http://blog.dooapp.com/javafx-composer-dynamic-layout-and-xscene
As I mentioned in a previous post, I was quite unsuccessful developing a resizable application with dynamic layout behavior using the beta release of the Netbeans composer. An Amy Fowler's post about Growing, Shrinking and Filling reminded me that I should give another try - dynamic sizing behavior is a great thing.
Moreover, some interesting changes have been introduced in the RC1 version of Netbeans. Let's see how to do this:
  • Create a new design file. By default the design file creates a javafx.scene.Scene. Unfortunately, this scene does not dynamically resize it's child nodes to it's current size.  This is exactly what the org.jfxtras.scene.XScene does. However, in the beta version of Netbeans Composer it was not possible to replace the default Scene by the one provided by JFXtras. Netbeans RC1 has introduced a cool feature: the Class Name Property. This allows to customize the class used in the generated code for a given component. This is a very interesting feature, bringing flexibility without overloading the composer's interface. This way you can customize any component handled by the composer (as long as it's using the same properties). In our case, just set "org.jfxtras.scene.XScene" as class scene for the scene component.
  • Next step is to add a layout manager to the scene. Let's use the Grid container, which is now supported by the RC1 version of the Composer! You can then add components to the layout and manage the number of rows and columns of the Grid layout. Notice that a new property has been introduced in the layout properties of the components: "Grid Hor. Span". This allows to control the spanning of a component over multiple columns. For example, in the attached screenshots, the bottom button is spanned over two columns. The left button switches it's Vertical Fill property, automatically adapting it's size to the new constraints.

You can now start building your application with dynamic sizing and layout using the JavaFX Composer - a big step forward!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Fri, 21 May 2010 05:34:00 -0700 Developing a business application with JavaFX Composer http://blog.dooapp.com/developing-a-business-application-with-javafx http://blog.dooapp.com/developing-a-business-application-with-javafx

When we started developing our first business application, I've hesitated a long time whether we should give a try to the Netbeans JavaFX composer. I am not a big fan of such gui design tools, especially because you usually get stuck sooner or later as your application grows. But I decided to give a chance to this new tool and got really convinced by the tutorials. So we started developing our application with this tool and we have now reached our first release. So let's have a review:

  • The first main question is about resizable layout. You can edit all the required layout information using the composer, but for the moment it's clearly more oriented for static layouts. So we decided to develop a fixed-size application. I hope future releases of JavaFX and of the composer will facilitate the development of resizable applications (for example by providing a resizable scene like the JFxtras' XScene)
  • The composer is designed to work with a single design file, i.e., your whole interface code is stored in a single JavaFX class. We quickly reached a point were we got a "code too large" error, due to the size of the class which is limited by the compiler. At this point I really thought I would stop using the composer, since I couldn't figure out how to split the design file without loosing the advantages of the composer. I have finally found an approach which proved to be quite handy using placeholders panels to integrate the nodes of separate design files. I'll post a separate blog article about this.
  • Another big drawback of the Composer is that you can't work with custom nodes (for examples with JFXtras controls). It would be really interesting to be able to manipulate custom nodes with the composer. For the moment the solution is to use a placeholder and to insert your component manually. As long as you use mostly standard JavaFX components this is not too annoying, but it can be quickly annoying if you work with many custom nodes or controls.
  • The state system is robust and provides a very nice way to manage the view according to the logic of the application. It's very easy and powerful to work with the states. The only point that was sometimes problematic is that you often edit some properties unintentionally in a specific state instead of in the master state. The "set to master" button is then really helpful.
  • Some minor features are still buggy. For example the edition of the Tooltip of a node does not work for the moment, clicking on the add button just lead to some Netbeans errors. However, this can still be done manually.
  • The auto-completion is unfortunately not provided when you edit the code of a binding through the property dialog, that would be handy.
  • Refactoring does not work in the code managed by the composer. So if your gui is bound to a model object and that you refactor the name of this object, the managed code won't be updated and you will have to do it manually through the composer gui.
To conclude I must admit that I've been really impressed by the Composer tool. It's stable and robust. The resulting JavaFX code is clear and remains clean even after hours playing with the composer. Some of the drawbacks and limitations I've enumerated are clearly annoying, but finally the composer is very very promising. I wouldn't have bet one month ago that we would have developed a business application with this tool. Now it's done, and I'm not sure I would be able to give up this composer so easily.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Sun, 02 May 2010 23:46:00 -0700 JavaFX chart API and logarithmic scale http://blog.dooapp.com/javafx-chart-api-and-logarithmic-scale-tag-ja http://blog.dooapp.com/javafx-chart-api-and-logarithmic-scale-tag-ja
 

JavaFX provides an impressive chart API that allow to draw a wide range of charts. Unfortunately the only scale that is available for XY charts is a linear implementation.  Since the javadoc of the API is sometimes a bit rough it took me some times to get figure out how to get a logarithmic scale. After a discussion on mix.oracle.com it turned out that there where to approaches: 

  • the first one is to transform your data with the logarithmic function before injecting it into the chart and to adjust the formatting of the labels to "simulate" a logarithmic scale. Since I don't really like the idea of manipulating the data to get the expected representation I prefer the second approach:
  • the second one is to provide your own implementation of a logarithmic Axis. Let's see how to do this:
Extend the ValueAxis class. There are two methods to implement:
public getDisplayPosition(valuejava.lang.Object) : Number 
 will do the mapping between a data value and its visual position. For a log axis, the implementation will look like this:
1
2
3
4
5
6
7
8
9
10
11
12
var logUpperBound: Number = bind Math.log10(upperBound);
var logLowerBound: Number = bind Math.log10(lowerBound);

override public function getDisplayPosition(arg0: Object): Number {
        def delta = logUpperBound - logLowerBound;
        def deltaV = Math.log10((arg0 as Number)) - logLowerBound;
        if (orientationVertical) {
            return (1 -((deltaV) / delta)) * height;
        } else {
            return ((deltaV) / delta) * width;
        }
    }

protected abstract updateTickMarks() : Void  will define which tick marks should be displayed. The javadoc does not say much about this function. It looks like the main idea is to update the tickMarks sequence of TickMark. The fields of TickMark are weirdly flagged as public-read package, so in oder to instantiate TickMark for our implementation we first need to define a class extending TickMark, let's say CustomTickMark, and to put it in the package javafx.scene.chart.part in order to be able to set the required fields. After that you just need to update the sequence of tick marks with the marks you want to see on your chart, which can look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
override protected function updateTickMarks(): Void {
        if (tickMarks.size() == 0) {
            for (i in [0..Math.log10(upperBound)-1], j in [1..9]) {
                var v = j * Math.pow(10, i);
                insert CustomTickMark {
                    customLabel: "{%6.0f v}"
                    customPosition: getDisplayPosition(v)
                    customValue: v
                } into tickMarks;
            }

        }
    }
Well, you're now ready to use this LogAxis with your chart. The following example demonstrate the use of this LogAxis as x-scale of a chart.
1
2
3
4
5
6
7
8
9
10
11
def logLineChart: javafx.scene.chart.LineChart = javafx.scene.chart.LineChart {
            data: [logData]
            xAxis: LogAxis {
                upperBound: 100
                lowerBound: 1
            }
            yAxis: NumberAxis {
                upperBound: 100
                lowerBound: 1
            }
        };
Log_scale

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Tue, 20 Apr 2010 07:27:00 -0700 JFXtras available in Sonatype OSS Repository http://blog.dooapp.com/jfxtras-available-in-sonatype-oss-repository http://blog.dooapp.com/jfxtras-available-in-sonatype-oss-repository

We have recently worked on a mavenization process of the JFXtras project to make it easily usable in a maven project. The project now has a pom and the current version (0.7-SNAPSHOT) is hosted at the Sonatype OSS repository.

To use JFXtras in your maven project, make sure that your maven environment has access to the Sonatype repository and just add the following dependencies to your pom:

EDIT: artifacts names have changed, they are now prepended with "jfxtras-"

1
2
3
4
5
<dependency>
  <groupId>org.jfxtras</groupId>
  <artifactId>jfxtras-common</artifactId>
  <version>0.7-SNAPSHOT</version>
</dependency>
1
2
3
4
5
<dependency>
  <groupId>org.jfxtras</groupId>
  <artifactId>jfxtras-controls</artifactId>
  <version>0.7-SNAPSHOT</version>
</dependency>

Hope this helps using this great lib!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Wed, 14 Apr 2010 03:08:03 -0700 Draggable nodes in XTreeView http://blog.dooapp.com/draggable-nodes-in-xtreeview http://blog.dooapp.com/draggable-nodes-in-xtreeview I recently played around with XTreeView the tree component of JFXtras. It's a nice component with a clean boundary between control and view so that was quiet easy to add a draggable behaviour to the nodes. By the way I'm really grateful to Rakesh Menon for his clear post about Drag and Drop in JavaFX.
The main idea is to wrap a renderer into a draggable wrapper that will encapsulate the created nodes into a SwingDragSource.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* A wrapper to a NodeRenderer adding draggable behaviour.
*
*/
public class DraggableNodeRenderer extends NodeRenderer {

/**
* The wrapped renderer
*/
    public-init var wrapped : NodeRenderer;
    
    /**
* Encapsulate the node created by the wrapped renderer into a SwingDragSource
*/
    override function createNode(data:XBind, index:Integer, editable:Boolean, width:Number, height:Number):Node {
        var dragSource: SwingDragSource;
        dragSource = SwingDragSource{
            content: wrapped.createNode(data, index, editable, width, height);
            transferData : data.ref
        }
    }
    
    /**
     * Delegate to the wrapped renderer
     */
    override function recycleNode(node: javafx.scene.Node) {
        wrapped.recycleNode(node);
    }

};
Checkout the demo: 
You will find the source and the eclipse project in the attached zip.

DnDTreeViewDemo.zip Download this file

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Fri, 19 Mar 2010 03:55:00 -0700 Just bind it http://blog.dooapp.com/just-bind-it http://blog.dooapp.com/just-bind-it

The javafx binding concept is nice and powerful. But sometimes it might be a bit difficult to figure out how to use it.

Let's assume you have the following bean
1
2
3
4
5
6
7
public class Bean {

    public var x:Number;

    public var y:Number;

}
and you need a variable bound to the sum of x*y for a sequence of those beans. Actually this problem can be expressed more generally: how to bind a variable to the result of a function applied to a sequence of elements (i.e. have it updated each time the sequence is modified or a field of a bean is modified). In java you would required tons of PropertyChangeListener registered correctly. With javafx you can write it in a more concise way using the bind keyword and bounded functions. But the first time I wanted to write it, I had to fight a little bit with the syntax. So I wanted to share this small unit test demonstrating a way to achieve this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class BindTest extends TestCase {

    var seq: Bean[];
    var tmp: Number[] = bind for (bean: Bean in seq) {
                sum(bean)
            };

    bound function sum(bean:Bean):Number {
        return bean.x * bean.y
    }

    var bean1:Bean = Bean {
        x: 2, y:4
    }

    var bean2:Bean = Bean {
        x: 1, y:2
    }

    function totalSum(tmp: Number[]): Number {
        var result: Number = 0.0;
        for (number: Number in tmp) {
            result += number;
        }
        return result
    }

    var result: Double = bind totalSum(tmp) on replace oldValue = newValue {
            println(newValue);
        }

    public function testBind() {
        println("Inserting bean 1");
        insert bean1 into seq;
        Assert.assertEquals(result, Double.valueOf(8));
        println("Inserting bean 2");
        insert bean2 into seq;
        Assert.assertEquals(result, Double.valueOf(10));
        println("Modifiying bean 1");
        bean1.x = 5;
        Assert.assertEquals(result, Double.valueOf(22));
    }

}

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Thu, 18 Mar 2010 10:06:59 -0700 MediaPlayer keeping quiet http://blog.dooapp.com/mediaplayer-keeping-quiet http://blog.dooapp.com/mediaplayer-keeping-quiet
According to the javafx media tutorial, playing a sound is simple. JavaFX provides a MediaPlayer to easily play any media source. You just need to define your Media by it's URI, so this should looks like:
1
2
3
4
5
6
var mediaPlayer: MediaPlayer = MediaPlayer {
            media: Media {
                source: "file:///test.mp3"
            }
        }
mediaPlayer.play();
This seems very easy, but if you tries this, you might here nothing!
Then you are certainly in one of the two following cases:
  • Your sound resource is packaged within your jar. This is not supported for the moment, see javafx faq
  • You are trying to play a short sound sample. The first time I tried to play a 2s sound sample, my speakers kept desperately quiet. So what happened? A look at the Improve Media Performance tutorial put me on the way to the solution: according to this article, "for faster playback of a streaming media that is progressively downloaded, it helps to initialize the engine first by using the same media behind the scenes". So, when you call the play function of the MediaPlayer, that playback engines "crunches" the first seconds of the sound sample during it's initialization. For a long sound, you just almost don't notice it, but a short sound sample might totally vanish. Unfortunately the MediaPlayer does not provides an init method, and the only way you can do it for the moment is to set your player to mute, play your media once and then rewind and unmute. It's quiet ugly, but it's the "official" solution proposed in the previous article. If you have a better solution, I'd be happy to hear it. Or just hope that javafx 1.3 will add an init method to the MediaPlayer.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Mon, 15 Mar 2010 08:46:44 -0700 Unit testing your javafx application http://blog.dooapp.com/unit-testing-your-javafx-application http://blog.dooapp.com/unit-testing-your-javafx-application In my last post I described how to set up a maven-javafx project using the jfrog-javafx plugin. One essential point was still missing in the configuration: the execution of javafx unit tests. By default, the jfrog-javafx plugin is not configured to handle a test folder. So let's see how to do that:
  • Create a src/test/javafx folder in your project
  • Put a unit test in this folder. Note that javafx does not support annotations, so the test must be written according to junit 3 rules. (however, you can use junit 4 in your dependencies, since it's backward compatible)
1
2
3
4
5
6
7
8
9
10
11
package com.dooapp.app;

import junit.framework.TestCase;

public class FirstTest extends TestCase{

public function test1(): Void{
println("Running a javafx test");
}

}
  • Update the configuration of the jfrog-javafx plugin to compile both the main and the test javafx folders. We define two executions for this plugin. The main execution will process the javafx resources and compile the javafx code in src/main/javafx during the compile phase. The test will compile the javafx code in src/test/javafx during the test-compile phase. You might also notice that junit is declared as a dependency of the plugin to fix a classpath issue since the jfrog-javafx plugin does not include the dependencies with test scope during compilation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<plugin>
<groupId>org.jfrog.maven.plugins</groupId>
<artifactId>jfrog-javafx-plugin</artifactId>
<version>1.1</version>
<executions>
<execution>
<id>main</id>
<phase>compile</phase>
<configuration>
<source>1.6</source>
<target>1.6</target>
<j2seVersion>1.5+</j2seVersion>
<encoding>UTF-8</encoding>
</configuration>
<goals>
<goal>resources</goal>
<goal>compile</goal>
<goal>jnlp</goal>
</goals>
</execution>
<execution>
<id>test</id>
<phase>test-compile</phase>
<configuration>
<source>1.6</source>
<target>1.6</target>
<j2seVersion>1.5+</j2seVersion>
<encoding>UTF-8</encoding>
<sourceDirectory>${basedir}/src/test/javafx</sourceDirectory>
<outputDirectory>${project.build.testOutputDirectory}</outputDirectory>
</configuration>
<goals>
<goal>resources</goal>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</plugin>
  • Now your javafx test should be executed during the test phase of the maven lifecycle like a regular java unit test. You can now let hudson run your javafx unit tests at each build!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Mon, 08 Mar 2010 07:38:52 -0800 Spice up your JavaFX project with maven http://blog.dooapp.com/spice-up-your-javafx-project-with-maven http://blog.dooapp.com/spice-up-your-javafx-project-with-maven
MavenFX.zip Download this file

JavaFX is a promising technology for nice Rich Internet Applications (RIA). However this is a quiet recent product (it was introduced by Sun at JavaOne, May 2007) and still lacks at integration with continuous development environments like hudson. Continuous integration is great because it reduces the gap between the development process and the release phase. With continuous integration, the work made by the developers is continuously compiled and tested against unit tests. This helps detecting many problems very early and simplifies the whole development process.

Hudson is an integration server that works very nicely with maven projects. So the first step to integrate a JavaFX project into our continuous integration environment was to "mavenize" the project. Jfrog has developed a javafx plugin for maven that integrates the compilation of the javafx sources into the maven lifecycle. Moreover it can generate a jnlp to launch the project. 

However, this plugin is by default configured to work with artifactory. For some reasons you might want to use it without artifactory, and that's possible. Just checkout the attached project to see how to configure it. Since artifactory takes care of some jnlp deployment issues, some additionnal plugins are required in this case: the dependency-plugin will copy the dependencies at the codebase directory of the jnlp and the jar-signer plugin will sign these jars.

At this step your project is fully integrated in the maven world. The last step is to deploy automatically the jnlp and required jars on a server to test your project. This can be achieved with the wagon plugin and its ftp extension (see attached project for an example of configuration). Then hudson can automatically deploy your application after each build by calling the mvn:deploy target. Enjoy!

Note: in the attached project, we use profiles (see profiles.xml) to configure the project dynamically whether this is intended to be run locally or deployed on a remote server. The "dev" profile allow you to run the project locally (using mvn clean package jfrog-javafx:excute) and the "prod" profile if for deployment (mvn -P-dev,prod deploy)

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Fri, 05 Feb 2010 05:30:00 -0800 Unit testing pages with Tapestry and Gaedo http://blog.dooapp.com/unit-testing-pages-with-tapestry-and-gaedo http://blog.dooapp.com/unit-testing-pages-with-tapestry-and-gaedo

Today we added our first page unit test using tapestry. Tapestry offer some nice mechanisms to easily test the rendered pages (see the tutorial); however some steps can be a bit tricky (essentially if you want to use the gaedo persistence layer in your tests) so I'll give a few hints here.

Tapestry provides a PageTester object that takes care about injection registry loading and page rendering.
The PageTester takes an app package name and app name as parameters in its constructor. Make sure you don't make a mistake in them, because you won't get any error even if the package or app module don't exist, so check it twice if your test is failing without evident reason.
To use the gaedo module, load it by adding it to the PageTester constructor. Moreover some configuration is required to work with a local database environment. All those steps can be done in an extension of the PageTester:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class GaedoPageTester extends PageTester {

private final static String APP_PACKAGE = "com.dooapp.ecomaison";
private final static String APP_NAME = "App";

public GaedoPageTester() {
super(APP_PACKAGE, APP_NAME);
ApiProxy.setEnvironmentForCurrentThread(new TestEnvironment());
ApiProxy.setDelegate(new ApiProxyLocalImpl(new File(".")) {
});
ApiProxyLocalImpl proxy = (ApiProxyLocalImpl) ApiProxy.getDelegate();
proxy.setProperty(LocalDatastoreService.NO_STORAGE_PROPERTY,
Boolean.TRUE.toString());
}

}
You can now use the render method of the GaedoPageTester to get the Document corresponding to a page and check it's content!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class PeopleTest {
 
private DynamicUserService userService;
 
private PageTester tester;
 
@Before
public void setUp() {
tester = new GaedoPageTester();
userService = tester.getService(DynamicUserService.class);
userService.create(new User("John"));
}
 
@Test
public void testPeoplePage() {
                // Render the People page
Document doc = tester.renderPage("People");
// check that the allUsers component has been rendered
Assert.assertNotNull(doc.getElementById("allUsers"));
}
 
        @Test
public void testJohnPeoplePage() {
                // Render the People page
Document doc = tester.renderPage("People/John");
// check that the profile component has been rendered
Assert.assertNotNull(doc.getElementById("userProfile"));
}
 
}
Last point: in the tapestry documentation you'll see that you must use the invoke method of the PageTester with a ComponentInvocation instance to provide a context to a given page. But if you use this, you'll get a compilation error, since the invoke method doesn't seem to exist yet/anymore (?). As explained in this thread, you can use the url passed to the render method to set the context. In the second test you can see we pass "John" as context to the "People" page by requesting the "People/John". Easy, isn't it? Happy testing with gaedo and tapestry!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Wed, 03 Feb 2010 09:29:00 -0800 Gaedo on Ohloh http://blog.dooapp.com/gaedo-on-ohloh http://blog.dooapp.com/gaedo-on-ohloh

The open source project Gaedo that we use as persistence layer for our project is now on Ohloh.com!

Gaedo is a persistence library providing dynamic code generation for your queries with strong typing. It's quick and easy to use. And dynamic code generation is awesome. Let's assume you need to retrieve your Users using their login field, you just need to define the following interface:
1
2
3
4
public interface DynamicUserService extends DynamicFinder<User, UserInformer>,
FinderCrudService<User, UserInformer> {
public User findOneWithLoginEqualsTo(String login);
}
and gaedo will do the rest for you, using the method and fields types and names! It couldn't be easier to use!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Mon, 01 Feb 2010 04:15:00 -0800 Running Tapestry5 + Maven on Google App Engine http://blog.dooapp.com/running-tapestry5-maven-on-google-app-engine http://blog.dooapp.com/running-tapestry5-maven-on-google-app-engine

 

1) Checking out Tapestry


As explained on the tapestry website , you should use the maven-tapestry-archetype

 

1
mvn archetype:generate -DarchetypeCatalog=http://tapestry.formos.com/maven-repository

While I'm writting this article, the lastest stable version of tapestry is 5.1.0.5, but there are an issue concerning tapestry/gae compatibility.

However, this bug is corrected in tapestry 5.1.0.6. If you still want to run tapestry 5.1.0.5 on GAE, you should use this quick fix :

Create a new class org.apache.tapestry5.internal.antlr.PropertyExpressionLexer and put this code on :


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
// $ANTLR 3.1.1 org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g 2009-12-10 22:52:32

package org.apache.tapestry5.internal.antlr;

import org.antlr.runtime.BaseRecognizer;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.DFA;
import org.antlr.runtime.EarlyExitException;
import org.antlr.runtime.FailedPredicateException;
import org.antlr.runtime.MismatchedSetException;
import org.antlr.runtime.NoViableAltException;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.RecognizerSharedState;

public class PropertyExpressionLexer extends
org.apache.tapestry5.internal.antlr.BaseLexer {
public static final int INTEGER = 4;
public static final int SIGN = 10;
public static final int RANGEOP = 6;
public static final int E = 19;
public static final int F = 20;
public static final int A = 18;
public static final int L = 23;
public static final int N = 24;
public static final int LETTER = 8;
public static final int H = 21;
public static final int NULL = 29;
public static final int I = 22;
public static final int U = 28;
public static final int T = 27;
public static final int DEREF = 5;
public static final int BANG = 16;
public static final int S = 26;
public static final int R = 25;
public static final int EOF = -1;
public static final int TRUE = 30;
public static final int LPAREN = 11;
public static final int LBRACKET = 13;
public static final int RPAREN = 12;
public static final int QUOTE = 17;
public static final int WS = 35;
public static final int SAFEDEREF = 34;
public static final int DECIMAL = 7;
public static final int COMMA = 15;
public static final int IDENTIFIER = 33;
public static final int THIS = 32;
public static final int NUMBER_OR_RANGEOP = 37;
public static final int DIGIT = 9;
public static final int RBRACKET = 14;
public static final int FALSE = 31;
public static final int STRING = 36;

// delegates
// delegators

public PropertyExpressionLexer() {
;
}

public PropertyExpressionLexer(CharStream input) {
this(input, new RecognizerSharedState());
}

public PropertyExpressionLexer(CharStream input, RecognizerSharedState state) {
super(input, state);

}

@Override
public String getGrammarFileName() {
return "org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g";
}

// $ANTLR start "INTEGER"
public final void mINTEGER() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:31:2:
// ()
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:31:4:
{
this.getClass(); /*
* Fix java.lang.VerifyError: Stack size too
* large
*/

}

} finally {
}
}

// $ANTLR end "INTEGER"

// $ANTLR start "DEREF"
public final void mDEREF() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:35:2:
// ()
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:35:4:
{
this.getClass(); /*
* Fix java.lang.VerifyError: Stack size too
* large
*/

}

} finally {
}
}

// $ANTLR end "DEREF"

// $ANTLR start "RANGEOP"
public final void mRANGEOP() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:39:2:
// ()
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:39:4:
{
this.getClass(); /*
* Fix java.lang.VerifyError: Stack size too
* large
*/

}

} finally {
}
}

// $ANTLR end "RANGEOP"

// $ANTLR start "DECIMAL"
public final void mDECIMAL() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:43:2:
// ()
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:43:4:
{
this.getClass(); /*
* Fix java.lang.VerifyError: Stack size too
* large
*/

}

} finally {
}
}

// $ANTLR end "DECIMAL"

// $ANTLR start "LETTER"
public final void mLETTER() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:46:2:
// ( ( 'a' .. 'z' | 'A' .. 'Z' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:46:4:
// ( 'a' .. 'z' | 'A' .. 'Z' )
{
if ((input.LA(1) >= 'A' && input.LA(1) <= 'Z')
|| (input.LA(1) >= 'a' && input.LA(1) <= 'z')) {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "LETTER"

// $ANTLR start "DIGIT"
public final void mDIGIT() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:48:2:
// ( '0' .. '9' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:48:4:
// '0' .. '9'
{
matchRange('0', '9');

}

} finally {
}
}

// $ANTLR end "DIGIT"

// $ANTLR start "SIGN"
public final void mSIGN() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:50:2:
// ( ( '+' | '-' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:50:4:
// ( '+' | '-' )
{
if (input.LA(1) == '+' || input.LA(1) == '-') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "SIGN"

// $ANTLR start "LPAREN"
public final void mLPAREN() throws RecognitionException {
try {
int _type = LPAREN;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:51:9:
// ( '(' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:51:11:
// '('
{
match('(');

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "LPAREN"

// $ANTLR start "RPAREN"
public final void mRPAREN() throws RecognitionException {
try {
int _type = RPAREN;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:52:9:
// ( ')' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:52:11:
// ')'
{
match(')');

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "RPAREN"

// $ANTLR start "LBRACKET"
public final void mLBRACKET() throws RecognitionException {
try {
int _type = LBRACKET;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:53:9:
// ( '[' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:53:11:
// '['
{
match('[');

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "LBRACKET"

// $ANTLR start "RBRACKET"
public final void mRBRACKET() throws RecognitionException {
try {
int _type = RBRACKET;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:54:9:
// ( ']' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:54:11:
// ']'
{
match(']');

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "RBRACKET"

// $ANTLR start "COMMA"
public final void mCOMMA() throws RecognitionException {
try {
int _type = COMMA;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:55:7:
// ( ',' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:55:9:
// ','
{
match(',');

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "COMMA"

// $ANTLR start "BANG"
public final void mBANG() throws RecognitionException {
try {
int _type = BANG;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:56:9:
// ( '!' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:56:11:
// '!'
{
match('!');

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "BANG"

// $ANTLR start "QUOTE"
public final void mQUOTE() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:59:2:
// ( '\\'' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:59:4:
// '\\''
{
match('\'');

}

} finally {
}
}

// $ANTLR end "QUOTE"

// $ANTLR start "A"
public final void mA() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:64:2:
// ( ( 'a' | 'A' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:64:4:
// ( 'a' | 'A' )
{
if (input.LA(1) == 'A' || input.LA(1) == 'a') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "A"

// $ANTLR start "E"
public final void mE() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:66:2:
// ( ( 'e' | 'E' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:66:4:
// ( 'e' | 'E' )
{
if (input.LA(1) == 'E' || input.LA(1) == 'e') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "E"

// $ANTLR start "F"
public final void mF() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:68:2:
// ( ( 'f' | 'F' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:68:4:
// ( 'f' | 'F' )
{
if (input.LA(1) == 'F' || input.LA(1) == 'f') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "F"

// $ANTLR start "H"
public final void mH() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:70:2:
// ( ( 'h' | 'H' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:70:4:
// ( 'h' | 'H' )
{
if (input.LA(1) == 'H' || input.LA(1) == 'h') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "H"

// $ANTLR start "I"
public final void mI() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:72:2:
// ( ( 'i' | 'I' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:72:4:
// ( 'i' | 'I' )
{
if (input.LA(1) == 'I' || input.LA(1) == 'i') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "I"

// $ANTLR start "L"
public final void mL() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:74:2:
// ( ( 'l' | 'L' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:74:5:
// ( 'l' | 'L' )
{
if (input.LA(1) == 'L' || input.LA(1) == 'l') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "L"

// $ANTLR start "N"
public final void mN() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:76:2:
// ( ( 'n' | 'N' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:76:4:
// ( 'n' | 'N' )
{
if (input.LA(1) == 'N' || input.LA(1) == 'n') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "N"

// $ANTLR start "R"
public final void mR() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:78:2:
// ( ( 'r' | 'R' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:78:4:
// ( 'r' | 'R' )
{
if (input.LA(1) == 'R' || input.LA(1) == 'r') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "R"

// $ANTLR start "S"
public final void mS() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:80:2:
// ( ( 's' | 'S' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:80:4:
// ( 's' | 'S' )
{
if (input.LA(1) == 'S' || input.LA(1) == 's') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "S"

// $ANTLR start "T"
public final void mT() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:82:2:
// ( ( 't' | 'T' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:82:4:
// ( 't' | 'T' )
{
if (input.LA(1) == 'T' || input.LA(1) == 't') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "T"

// $ANTLR start "U"
public final void mU() throws RecognitionException {
try {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:84:2:
// ( ( 'u' | 'U' ) )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:84:4:
// ( 'u' | 'U' )
{
if (input.LA(1) == 'U' || input.LA(1) == 'u') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}

} finally {
}
}

// $ANTLR end "U"

// $ANTLR start "NULL"
public final void mNULL() throws RecognitionException {
try {
int _type = NULL;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:88:7:
// ( N U L L )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:88:9:
// N U L L
{
mN();
mU();
mL();
mL();

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "NULL"

// $ANTLR start "TRUE"
public final void mTRUE() throws RecognitionException {
try {
int _type = TRUE;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:89:6:
// ( T R U E )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:89:8:
// T R U E
{
mT();
mR();
mU();
mE();

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "TRUE"

// $ANTLR start "FALSE"
public final void mFALSE() throws RecognitionException {
try {
int _type = FALSE;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:90:7:
// ( F A L S E )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:90:9:
// F A L S E
{
mF();
mA();
mL();
mS();
mE();

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "FALSE"

// $ANTLR start "THIS"
public final void mTHIS() throws RecognitionException {
try {
int _type = THIS;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:91:6:
// ( T H I S )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:91:8:
// T H I S
{
mT();
mH();
mI();
mS();

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "THIS"

// $ANTLR start "IDENTIFIER"
public final void mIDENTIFIER() throws RecognitionException {
try {
int _type = IDENTIFIER;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:94:2:
// ( LETTER ( LETTER | DIGIT | '_' )* )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:94:4:
// LETTER ( LETTER | DIGIT | '_' )*
{
mLETTER();
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:94:11:
// ( LETTER | DIGIT | '_' )*
loop1: do {
int alt1 = 2;
int LA1_0 = input.LA(1);

if (((LA1_0 >= '0' && LA1_0 <= '9')
|| (LA1_0 >= 'A' && LA1_0 <= 'Z') || LA1_0 == '_' || (LA1_0 >= 'a' && LA1_0 <= 'z'))) {
alt1 = 1;
}

switch (alt1) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:
{
if ((input.LA(1) >= '0' && input.LA(1) <= '9')
|| (input.LA(1) >= 'A' && input.LA(1) <= 'Z')
|| input.LA(1) == '_'
|| (input.LA(1) >= 'a' && input.LA(1) <= 'z')) {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}
break;

default:
break loop1;
}
} while (true);

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "IDENTIFIER"

// $ANTLR start "SAFEDEREF"
public final void mSAFEDEREF() throws RecognitionException {
try {
int _type = SAFEDEREF;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:100:2:
// ( '?.' )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:100:5:
// '?.'
{
match("?.");

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "SAFEDEREF"

// $ANTLR start "WS"
public final void mWS() throws RecognitionException {
try {
int _type = WS;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:102:5:
// ( ( ' ' | '\\t' | '\\n' | '\\r' )+ )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:102:7:
// ( ' ' | '\\t' | '\\n' | '\\r' )+
{
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:102:7:
// ( ' ' | '\\t' | '\\n' | '\\r' )+
int cnt2 = 0;
loop2: do {
int alt2 = 2;
int LA2_0 = input.LA(1);

if (((LA2_0 >= '\t' && LA2_0 <= '\n') || LA2_0 == '\r' || LA2_0 == ' ')) {
alt2 = 1;
}

switch (alt2) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:
{
if ((input.LA(1) >= '\t' && input.LA(1) <= '\n')
|| input.LA(1) == '\r' || input.LA(1) == ' ') {
input.consume();

} else {
MismatchedSetException mse = new MismatchedSetException(
null, input);
recover(mse);
throw mse;
}

}
break;

default:
if (cnt2 >= 1)
break loop2;
EarlyExitException eee = new EarlyExitException(2,
input);
throw eee;
}
cnt2++;
} while (true);

skip();

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "WS"

// $ANTLR start "STRING"
public final void mSTRING() throws RecognitionException {
try {
int _type = STRING;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:107:2:
// ( QUOTE ( options {greedy=false; } : . )* QUOTE )
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:107:4:
// QUOTE ( options {greedy=false; } : . )* QUOTE
{
mQUOTE();
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:107:10:
// ( options {greedy=false; } : . )*
loop3: do {
int alt3 = 2;
int LA3_0 = input.LA(1);

if ((LA3_0 == '\'')) {
alt3 = 2;
} else if (((LA3_0 >= '\u0000' && LA3_0 <= '&') || (LA3_0 >= '(' && LA3_0 <= '\uFFFF'))) {
alt3 = 1;
}

switch (alt3) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:107:37:
// .
{
matchAny();

}
break;

default:
break loop3;
}
} while (true);

mQUOTE();
setText(getText().substring(1, getText().length() - 1));

}

state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "STRING"

// $ANTLR start "NUMBER_OR_RANGEOP"
public final void mNUMBER_OR_RANGEOP() throws RecognitionException {
try {
int _type = NUMBER_OR_RANGEOP;
int _channel = DEFAULT_TOKEN_CHANNEL;
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:120:2:
// ( ( SIGN )? ( DIGIT )+ ({...}? => '.' ( DIGIT )* | ) | SIGN '.' (
// DIGIT )+ | '.' ( ( DIGIT )+ | '.' | ) )
int alt11 = 3;
switch (input.LA(1)) {
case '+':
case '-': {
int LA11_1 = input.LA(2);

if (((LA11_1 >= '0' && LA11_1 <= '9'))) {
alt11 = 1;
} else if ((LA11_1 == '.')) {
alt11 = 2;
} else {
NoViableAltException nvae = new NoViableAltException("",
11, 1, input);

throw nvae;
}
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
alt11 = 1;
}
break;
case '.': {
alt11 = 3;
}
break;
default:
NoViableAltException nvae = new NoViableAltException("", 11, 0,
input);

throw nvae;
}

switch (alt11) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:120:4:
// ( SIGN )? ( DIGIT )+ ({...}? => '.' ( DIGIT )* | )
{
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:120:4:
// ( SIGN )?
int alt4 = 2;
int LA4_0 = input.LA(1);

if ((LA4_0 == '+' || LA4_0 == '-')) {
alt4 = 1;
}
switch (alt4) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:120:4:
// SIGN
{
mSIGN();

}
break;

}

// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:120:10:
// ( DIGIT )+
int cnt5 = 0;
loop5: do {
int alt5 = 2;
int LA5_0 = input.LA(1);

if (((LA5_0 >= '0' && LA5_0 <= '9'))) {
alt5 = 1;
}

switch (alt5) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:120:10:
// DIGIT
{
mDIGIT();

}
break;

default:
if (cnt5 >= 1)
break loop5;
EarlyExitException eee = new EarlyExitException(5,
input);
throw eee;
}
cnt5++;
} while (true);

// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:121:3:
// ({...}? => '.' ( DIGIT )* | )
int alt7 = 2;
int LA7_0 = input.LA(1);

if ((LA7_0 == '.') && ((input.LA(2) != '.'))) {
alt7 = 1;
} else {
alt7 = 2;
}
switch (alt7) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:122:4:
// {...}? => '.' ( DIGIT )*
{
if (!((input.LA(2) != '.'))) {
throw new FailedPredicateException(input,
"NUMBER_OR_RANGEOP", " input.LA(2) != '.' ");
}
match('.');
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:122:35:
// ( DIGIT )*
loop6: do {
int alt6 = 2;
int LA6_0 = input.LA(1);

if (((LA6_0 >= '0' && LA6_0 <= '9'))) {
alt6 = 1;
}

switch (alt6) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:122:35:
// DIGIT
{
mDIGIT();

}
break;

default:
break loop6;
}
} while (true);

_type = DECIMAL;
stripLeadingPlus();

}
break;
case 2:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:123:6:
{
_type = INTEGER;
stripLeadingPlus();

}
break;

}

}
break;
case 2:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:126:4:
// SIGN '.' ( DIGIT )+
{
mSIGN();
match('.');
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:126:13:
// ( DIGIT )+
int cnt8 = 0;
loop8: do {
int alt8 = 2;
int LA8_0 = input.LA(1);

if (((LA8_0 >= '0' && LA8_0 <= '9'))) {
alt8 = 1;
}

switch (alt8) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:126:13:
// DIGIT
{
mDIGIT();

}
break;

default:
if (cnt8 >= 1)
break loop8;
EarlyExitException eee = new EarlyExitException(8,
input);
throw eee;
}
cnt8++;
} while (true);

_type = DECIMAL;
stripLeadingPlus();

}
break;
case 3:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:128:4:
// '.' ( ( DIGIT )+ | '.' | )
{
match('.');
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:129:3:
// ( ( DIGIT )+ | '.' | )
int alt10 = 3;
switch (input.LA(1)) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
alt10 = 1;
}
break;
case '.': {
alt10 = 2;
}
break;
default:
alt10 = 3;
}

switch (alt10) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:130:4:
// ( DIGIT )+
{
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:130:4:
// ( DIGIT )+
int cnt9 = 0;
loop9: do {
int alt9 = 2;
int LA9_0 = input.LA(1);

if (((LA9_0 >= '0' && LA9_0 <= '9'))) {
alt9 = 1;
}

switch (alt9) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:130:4:
// DIGIT
{
mDIGIT();

}
break;

default:
if (cnt9 >= 1)
break loop9;
EarlyExitException eee = new EarlyExitException(9,
input);
throw eee;
}
cnt9++;
} while (true);

_type = DECIMAL;
stripLeadingPlus();

}
break;
case 2:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:131:6:
// '.'
{
match('.');
_type = RANGEOP;

}
break;
case 3:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:132:6:
{
_type = DEREF;

}
break;

}

}
break;

}
state.type = _type;
state.channel = _channel;
} finally {
}
}

// $ANTLR end "NUMBER_OR_RANGEOP"

@Override
public void mTokens() throws RecognitionException {
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:8: (
// LPAREN | RPAREN | LBRACKET | RBRACKET | COMMA | BANG | NULL | TRUE |
// FALSE | THIS | IDENTIFIER | SAFEDEREF | WS | STRING |
// NUMBER_OR_RANGEOP )
int alt12 = 15;
alt12 = dfa12.predict(input);
switch (alt12) {
case 1:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:10:
// LPAREN
{
mLPAREN();

}
break;
case 2:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:17:
// RPAREN
{
mRPAREN();

}
break;
case 3:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:24:
// LBRACKET
{
mLBRACKET();

}
break;
case 4:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:33:
// RBRACKET
{
mRBRACKET();

}
break;
case 5:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:42:
// COMMA
{
mCOMMA();

}
break;
case 6:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:48:
// BANG
{
mBANG();

}
break;
case 7:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:53:
// NULL
{
mNULL();

}
break;
case 8:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:58:
// TRUE
{
mTRUE();

}
break;
case 9:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:63:
// FALSE
{
mFALSE();

}
break;
case 10:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:69:
// THIS
{
mTHIS();

}
break;
case 11:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:74:
// IDENTIFIER
{
mIDENTIFIER();

}
break;
case 12:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:85:
// SAFEDEREF
{
mSAFEDEREF();

}
break;
case 13:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:95:
// WS
{
mWS();

}
break;
case 14:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:98:
// STRING
{
mSTRING();

}
break;
case 15:
// org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g:1:105:
// NUMBER_OR_RANGEOP
{
mNUMBER_OR_RANGEOP();

}
break;

}

}

protected DFA12 dfa12 = new DFA12(this);
static final String DFA12_eotS = "\7\uffff\3\12\5\uffff\10\12\1\33\1\34\1\35\1\12\3\uffff\1\37\1\uffff";
static final String DFA12_eofS = "\40\uffff";
static final String DFA12_minS = "\1\11\6\uffff\1\125\1\110\1\101\5\uffff\1\114\1\125\1\111\2\114"
+ "\1\105\2\123\3\60\1\105\3\uffff\1\60\1\uffff";
static final String DFA12_maxS = "\1\172\6\uffff\1\165\1\162\1\141\5\uffff\1\154\1\165\1\151\2\154"
+ "\1\145\2\163\3\172\1\145\3\uffff\1\172\1\uffff";
static final String DFA12_acceptS = "\1\uffff\1\1\1\2\1\3\1\4\1\5\1\6\3\uffff\1\13\1\14\1\15\1\16\1\17"
+ "\14\uffff\1\7\1\10\1\12\1\uffff\1\11";
static final String DFA12_specialS = "\40\uffff}>";
static final String[] DFA12_transitionS = {
"\2\14\2\uffff\1\14\22\uffff\1\14\1\6\5\uffff\1\15\1\1\1\2\1"
+ "\uffff\1\16\1\5\2\16\1\uffff\12\16\5\uffff\1\13\1\uffff\5\12"
+ "\1\11\7\12\1\7\5\12\1\10\6\12\1\3\1\uffff\1\4\3\uffff\5\12\1"
+ "\11\7\12\1\7\5\12\1\10\6\12", "", "", "", "", "", "",
"\1\17\37\uffff\1\17",
"\1\21\11\uffff\1\20\25\uffff\1\21\11\uffff\1\20",
"\1\22\37\uffff\1\22", "", "", "", "", "", "\1\23\37\uffff\1\23",
"\1\24\37\uffff\1\24", "\1\25\37\uffff\1\25",
"\1\26\37\uffff\1\26", "\1\27\37\uffff\1\27",
"\1\30\37\uffff\1\30", "\1\31\37\uffff\1\31",
"\1\32\37\uffff\1\32",
"\12\12\7\uffff\32\12\4\uffff\1\12\1\uffff\32\12",
"\12\12\7\uffff\32\12\4\uffff\1\12\1\uffff\32\12",
"\12\12\7\uffff\32\12\4\uffff\1\12\1\uffff\32\12",
"\1\36\37\uffff\1\36", "", "", "",
"\12\12\7\uffff\32\12\4\uffff\1\12\1\uffff\32\12", "" };

static final short[] DFA12_eot = DFA.unpackEncodedString(DFA12_eotS);
static final short[] DFA12_eof = DFA.unpackEncodedString(DFA12_eofS);
static final char[] DFA12_min = DFA
.unpackEncodedStringToUnsignedChars(DFA12_minS);
static final char[] DFA12_max = DFA
.unpackEncodedStringToUnsignedChars(DFA12_maxS);
static final short[] DFA12_accept = DFA.unpackEncodedString(DFA12_acceptS);
static final short[] DFA12_special = DFA
.unpackEncodedString(DFA12_specialS);
static final short[][] DFA12_transition;

static {
int numStates = DFA12_transitionS.length;
DFA12_transition = new short[numStates][];
for (int i = 0; i < numStates; i++) {
DFA12_transition[i] = DFA.unpackEncodedString(DFA12_transitionS[i]);
}
}

class DFA12 extends DFA {

public DFA12(BaseRecognizer recognizer) {
this.recognizer = recognizer;
this.decisionNumber = 12;
this.eot = DFA12_eot;
this.eof = DFA12_eof;
this.min = DFA12_min;
this.max = DFA12_max;
this.accept = DFA12_accept;
this.special = DFA12_special;
this.transition = DFA12_transition;
}

@Override
public String getDescription() {
return "1:1: Tokens : ( LPAREN | RPAREN | LBRACKET | RBRACKET | COMMA | BANG | NULL | TRUE | FALSE | THIS | IDENTIFIER | SAFEDEREF | WS | STRING | NUMBER_OR_RANGEOP );";
}
}

}

 

2) Configuring the project for GAE


Drop a file named "appengine-web.xml" in src/main/webapp/WEB-INF

The file should looks like this :

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>PROJECT NAME</application>
<version>PROJECT VERSION WITHOUT "."</version>

<!-- Configure java.util.logging -->
<system-properties>
<property name="java.util.logging.config.file" value="log4j.properties" />
</system-properties>
<sessions-enabled>true</sessions-enabled>
</appengine-web-app>

If you want to know how to synchronize GAE and maven version you should read this excellent article writed by Antoine : http://blog.dooapp.com/synchronize-maven-and-gae-application-version

Of course, you'll need to download the GAE SDK for java from here : http://code.google.com/intl/fr/appengine/downloads.html

3) Running the tapestry project on gae


Before starting to code, I suggest you to try running the simple tapestry example on gae.

 

  • Go to your project
  • mvn package
  • go to GAEHOME/bin
  • ./appcfg.sh update YOURPROJECTHOME/target/YOURPROJECTNAME
  • Enter your mail and password
  • go to http://YOURPROJECTNAME.appspot.com

For now, there are some issues when you run the gae server on your localhost. So I suggest you to test everything you do online.

4) What's Next


Now you can run tapestry on gae, if you want to upload your application with maven, I suggest you to have a look at this plugin : http://code.google.com/p/maven-gae-plugin/

And of course configuring a continuous integration server can be very useful to automatically build and deploy your application: http://hudson-ci.org/

 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1349933/christophe2_no_background.png http://posterous.com/users/3snAL9uOrKWl Christophe Dufour Christophe Christophe Dufour
Fri, 29 Jan 2010 08:40:34 -0800 Gaedo, tapestry and mutual injection: where Proxies solve the egg and chicken problem http://blog.dooapp.com/gaedo-tapestry-and-mutual-injection-where-pro http://blog.dooapp.com/gaedo-tapestry-and-mutual-injection-where-pro I was thinking about how to integrate the gaedo library with our tapestry project. Gaedo is a persistance library providing dynamic finders.
To use gaedo properly with tapestry my first guess was to define a module (in tapestry terms) providing the required ServiceRepository which is the main entry point for gaedo. The GaedoModule class defines that binding for this interface using a build() method:
1
2
3
4
5
6
7
8
public static ServiceRepository build(
Collection<FinderCrudService> configuration) {
ServiceRepository serviceRepository = new SimpleServiceRepository();
for (FinderCrudService finderCrudService : configuration) {
serviceRepository.add(finderCrudService);
}
return serviceRepository;
}
With the right entry com.dooapp.gaedo.tapestry.GaedoModule: com.dooapp.gaedo.tapestry.GaedoModule in the Manifest.mf file, dropping the jar of the gaedo-tapestry module in our tapestry application classpath is enough to get our module automatically loaded.
As you can see in the build method, we expect a Collection<FinderCrudService> configuration to configure the ServiceRepository properly. This parameter can be provided through the tapestry mechanism for service configuration. So in the AppModule of our tapesty application we just need to define a method call contributeServiceRepository to provide the required configuration to our ServiceRepository.
1
2
3
4
5
6
public static void contributeServiceRepository(
Configuration<FinderCrudService> configuration,
ServiceRepository repository) {
configuration.add(new DatastoreFinderServiceImpl<User, UserInformer>(
User.class, UserInformer.class, repository));
}
You might have noticed that we get the ServiceRepository injected in this method. Weird? Who will be instanciated first? The ServiceRepostiory that requires the collection of FinderCrudService to be fully configured or the FinderCurdService which needs itself the ServiceRepostory to be instanciated?

I was thinking some hours about how to solve this problem, trying to remove some dependencies when I stumbled upon the documentation about mutually dependent services injection with the tapestry IoC framework. And the conclusion is: it *just* works! I had nothing to do! This mutual injection just perfectly works out of box! Amazing! The big point is that the tapestry IoC framework is working with Proxies to instantiate objects, and that's the key of this egg and chicken mistery. Both service can be instantiated in any order, you don't need to care about it. It will work.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Thu, 21 Jan 2010 01:47:00 -0800 Synchronize maven and gae application version http://blog.dooapp.com/synchronize-maven-and-gae-application-version http://blog.dooapp.com/synchronize-maven-and-gae-application-version

We are currently developing a maven project deployed on google appengine.Maven manages a version property in its pom.xml:

1
2
3
4
5
6
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.dooapp</groupId>
<artifactId>myproject</artifactId>
<version>0.0.1-SNAPSHOT</version>
</project>

When we release the project with maven (mvn release:prepare and mvn release:perform), this version number gets automatically increased to the next development version, for example from 0.0.1-SNAPSHOT to 0.0.2-SNAPSHOT.
The main problem is that google app engine uses another version descriptor in the appengine-web.xml file that is independent from the first one:
1
2
3
4
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>myproject</application>
<version>0-0-1</version>
</appengine-web-app>

It would be great to get those to versions number synchronized! Here is a solution how to get it working:

  • Define a tiny groovy script, let's say utils.groovy, that will define a property defining the gae version. The problem is that we can't use the maven version directly since gae does not accept '.' and '-SNAPSHOT' in it's version. So this script will map the gae version to the maven version in that way:  X.Y.Z(-SNAPSHOT) -> X-Y-Z . Of course we will lose the SNAPSHOT information, but this is not really relevant in gae terms. Your application will be deployed at X-Y-Z.latest.myproject-site.appspot.com.

1
2
3
4
5
def gaeversion = new String(project.version).replace('.','-')
if (gaeversion.endsWith("SNAPSHOT")) {
gaeversion = gaeversion.substring(0, gaeversion.length() - 9)
}
project.properties['gaeversion'] = gaeversion

  • Attach the execution of the groovy script to the maven validate phase adding this to the pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>src/main/groovy/utils.groovy</source>
</configuration>
</execution>
</executions>
</plugin>

  • This will define a maven property named gaeversion that you can use in your appengine-web.xml file

1
2
3
4
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>myproject</application>
<version>${gaeversion}</version>
</appengine-web-app>

  • And finally tell to maven to filter our appengine-web.xml resource!

1
2
3
4
5
6
7
8
9
10
11
12
13
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<webResource>
<directory >
src/main/webapp
</directory>
<filtering>true</filtering>
</webResource>
</webResources>
</configuration>
</plugin>

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler
Tue, 19 Jan 2010 13:44:01 -0800 Say "hey joe" http://blog.dooapp.com/say-hey-joe http://blog.dooapp.com/say-hey-joe
Hey_joe

Now to talk with hudson, we have just have to write this little sentence : "hey joe help"  and then John Hudson will be here for us

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/1349933/christophe2_no_background.png http://posterous.com/users/3snAL9uOrKWl Christophe Dufour Christophe Christophe Dufour
Tue, 19 Jan 2010 10:12:00 -0800 John hudson and continuous integration http://blog.dooapp.com/10200583 http://blog.dooapp.com/10200583

Capture

We were thinking about a solution to keep up our google chatroom up even we all get disconnected. We were thinking about a bot when I remembered about a jabber plugin for hudson - actually, if we use a bot on the chatroom, why not hudson?

So we installed the plugin and after some configuration (make sure you configured it both in the global hudson configuration and in the project configuration, since it is not activated by default), we finally welcomed John Hudson as new colleague in our chatroom. This way we get informed of various events (build start, build failed,...fully configurable) directly in the chatroom.
And you know the best? We can even control hudson from jabber using simple commands such as !build myproject 
Simple and amazing. Continuous integration is great!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/383766/logo_dooapp_big.jpg http://posterous.com/users/3snzuaSliuWt Antoine Mischler Antoine Antoine Mischler