Wednesday, February 17, 2010

Next steps with git...

So after doing a checkout of the repository using git-svn for over two hours I started looking for an eclipse plugin for git.

What I found was EGit, which installed fine... but... well none of the git options appeared. Neither can I import from a git repository nor can I use git commands on the ready project. I tried to install about 5 times and failed 5 times. Maybe it is an error with the plugin, or something with eclipse is making a problem - I really don't know. The error log is clean.

So for me the one and only existing Git plugin for eclipse I found is unusable for me atm.... Next minus for git.

In fact I am now wondering about how to use something equal to for example RapidSVN. A diff tool, that allows me to remove different changes is very important to me - doing that only on the command line is not very effective.

Tuesday, February 16, 2010

Trying out Git

My current work requires a lot of changes and experiments so I thought it might be good to try out git with the subversion back end.

So the first step is to make a local repository and then get the current svn repository... uhm, yes, the complete repository. Well ok, I am chekcing out only for trunk here, but that is the branch with the most traffic. NOw trunk is atm around revision 19250. A lot of those are not relevant for trunk, since it is about making branches or the test system making commits. But even if a third of those are normal revisions, we still talk about over 6000 revision. Revision git will all have to download.

Now after 45 Minutes I am at about revision 2350... 17000 to go... ehm... this will take hours it seems.

Ok, you have to do that part only once, but still... This I take as a minus for git vs. svn

Tuesday, November 10, 2009

Static Groovy - about calling methods

After a long time I am now here writing something in my blog again. In the past I talked mostly about new features and possibilities like for example http://blackdragsview.blogspot.com/2006/09/groovy-on-speed-fast-mode-for-groovy.html

I want to talk about a static mode for Groovy here, since this is a reoccurring thing on the lists. I get the feeling people don't understand exactly what I mean and I want to try and explain it here a bit more. Of course I have my knowledge and others have their knowledge and maybe I will tell something that is wrong here. Well, if that happens tell me, I am aware of the fact that I know only a small fraction. And maybe one of the readers here knows just the way I was searching for and unable to find.

One basic part in Groovy is the method call. Almost any operator results in a method call and many structures are simulated through method calls. Now Groovy is a language with what I call instance based multi methods. Let me give an example:

class A {
def foo(Object x){ bar(x) }
def bar(Object o){1}
}
class B extends A {
def bar(String s){2}
}
def a = new A()
assert a.bar("x") ==1
assert a.foo("x") ==1
def b = new B()
assert b.bar("x") ==2
assert b.foo("x") ==2

Now looking at that code you might realize, that Java would fail in the last assert for multiple reasons. First foo takes an Object typed parameter and that is never going to call a String based method, because the static type of the parameter is used as type for the argument, which then influences the method preselection process the compiler does. So Java would always select the bar(Object) method. The other aspect is that bar(String) is not declared in A, but in B. If you want another method get called in Java, you have to override one from the parent class, but bar(String) is not overriding in the Java sense of inheritance. So in Java you would in class B write a bar(Object) method, that checks if the given argument has the runtime type String and then dispatch to the String taking method else to super.bar(Object). In Groovy this is not needed. If bar is final, then in Java you cannot do that additional dispatch, for Groovy this makes no difference.

I should maybe also mention that often people talk about dynamic dispatch or dynamic method calls. What some mean is that from a given set of methods of a given class a method is selected using the runtime types of all involved arguments and the receiver. They often don't realize that this is multi methods. Coming from a Java thinking they assume only methods from class A can be taken, since foo is defined in A and so a bar of A must be used. In Java you might add that this is always true unless a subclass is overriding the method, but no new method signatures will be added to the set. Only, that if you use the dynamic type of the receiver anyway (yes, Java does this!) why make methods of it invisible? That was a decision made by the Java people and I am sure they had their reason to do so, but this is no requirement for a static language, especially if the return types cannot differ Before Generics were introduced this was the case for Java. Even for current Java I think it would be possible to ad. And if you take a look at MultiJava for example, you might realize that it does not have to be a dynamic feature either.


Another aspect of Groovy and method calls is the invokeMethod/methodMissing logic. Basically the method is called if the goal method couldn't be found. The parameters for this search are based on the dynamic types and multi methods, but a static language could have a methodMissing as well. But what would that mean?

A static mode for Groovy is something all those people like to have, that either think the compiler should do more checks or that Groovy should have more speed. Let us concentrate on compile time checks here. A common example is the misspelling of a method name or a method name gets changed. Let us take the example Chris Richardson in his Community One East 09 talk gave http://www.slideshare.net/chris.e.richardson/communityoneeast-09-dynamic-languages-the-next-big-thing-for-the-jvm-or-an-evolutionary-dead-end On slide 30 executeRequest got renamed to executeEc2Request and he complains about his test not picking up the problem, where it should have been a job of the compiler anyway.

Now let us imagine a language in which every method call, that does not point to a specific method, the compiler will create a call to method missing. In such a language a change of the signature of one method will most probably not lead to a compilation error. Instead the method call will now call method missing. In other words: One of the most important features, the check of a method call, will not work.

Still it wouldn't be worthless since in refactorings you could still have the method being changed automatically you might say. But this is an IDE-thing and not a compiler thing. As such it is only partially concern of the language and more of the IDE being able to do something in such cases.
class A {
def bar() {1}
}
def a = new A()
a.bar()
Let us assume this is the code in your IDE and you want to refactor A.bar into A.foo. An IDE could through type inference know that "a" will be of type A and as such know that the call a.bar() would normally have called A.bar and that if I rename that method, the IDE should change a.bar() into a.foo(). Also if the example would have been
class A {
def bar() {1}
}
def exec(x){x.bar()}
def a = new A()
exec(a)
the IDE would have had a much tougher job. But an IDE knows all the code that is involved, so it could try to identify all types that are used to call exec and then distill a common super type for it. So in the end it could still find out, that exec is called with A and since we refactor A.foo it could still find that it should be changed to A.bar. It is difficult, but not impossible for the IDE to set things right here as we can resolve the issue with just type inference. Let us go even one step further
class A {
def bar() {1}
}
class B {
def bar() {2]
}
def exec(x){x.bar()}
def a = new A()
exec(a)
def b = new B()
exec(b)
Here A and B have no common super type that contains the definition of bar, still a name change for A.bar would affect the program. The right thing here to do would be to also change the name of B.bar. If the IDE knows how exec is called and with what types these calls are made, it can find this out. There might be cases in which the IDE won't be able to find the right types, but I am positive that in most cases the IDE will be able to just do that. If such type inference is available, then auto completion for method signatures will work too. So the biggest use cases in an IDE, renaming and completion, can work out just fine in most cases even with Groovy.

So if method missing has no positive affect and multi methods are restricted, then what is left is the normal method call logic Java uses. And the question here is if that is worth it.

Saturday, October 13, 2007

Java 5 features in Groovy

Groovy 1.1 RC-1 has been released yesterday and I thought this is a perfect time for giving a complete list of all the Java 5 features that are available in Groovy and how much of it. I will not explain the Java 5 features itself, I will just show what you can do and what not. So let us start right away:

Variable Arguments

Groovy is able to use this for a long time now. Since Groovy does not know exactly which method it will invoke at compile time we use a vargs (variable arguments) when selecting a method for invocation. but not only the vargs Java has...I mean these things with the funny little triple dotted type as last parameter, no Groovy just requires an array there. so if you want to use a vargs method from Groovy just do it like in Java. vargs methods defined in Groovy can be used as such from Groovy too. Currently Groovy does not add the special modifier for vargs to the array, so it won't be a vargs method for Java, just a method taking an array as last parameter. I just filled an issue for that and it will be fixed before the 1.1 release. Ah yes, before I forget... of course you can use the triple dot notation instead of using an array in Groovy too. The tripple dot notation will be exactly the same as an array as last parameter.

To use vargs no java5 vm is needed from the groovy side.

New For-Loop

Groovy had always a for-loop in that style, only we don't use only the ":", we use allow also the usage of the keyword"in" at that place. The Groovy for-loop asks for an iterator by calling the iterator() method, which is available on any object. Groovy adds a dynamic iterator() method to the MetaClass for every class as long as the class does not define it on its own.

To use the new for-loop no java5 vm is needed from the groovy side.

Covariant Return Types

It took a while before Groovy was able to use these (available since 1.1 RC-1). Originally we wanted to be able to overload a method if the return type differs, not override it. But it seemed to make no sense to differ here from Java. So Groovy follows here Java now and if you write a subclass, that has a method that matches a method in the parent class in name and argument types, but has a different return type, then covariants kick in. If the the new method has a return type derived from the old method, then the new method overrides the old method. If it does not derive from that, then you get a compile time error. As an example:

class A{
Object foo(){1}
}
class B extends A{
String foo() {2}
}
def b=new B()
assert b.foo()==2
To use covariant return types no java5 vm is needed from the groovy side.

Annotations

Again these are available for some time now. The syntax is equal to Java. Even though you can define interfaces in Groovy you can not define an annotation in Groovy yet. But using Annotations in Groovy is basically the same as in Java.

If annotations are used Groovy needs to produce java5 bytecode.

Generics

Well... originally we didn't want to add them. They are complicated and look kind of surplus in a dynamic world. But some applications can make use of generics. for example List<book> books contains much more informations for a persistence layer like for example JPA. But maybe I should first explain that we do not support checks where type erasure is used. For example if you define a script like
List<String> list = ["a","b"]
list << 1
then Groovy won't complain. This is simply ignored. But if you use generics for a method, a field or in the class header (class X<A,B> extends Y<A> implents Z<B>), then Groovy will write the information in bytecode the same way as Java would do. That means if you use the Groovy class in Java, you have the same pain... ehm pleasure... what ever... as with a class defined in Java. With the 1.1 RC-1 release Groovy now also checks the header information, so you won't be able to write class X extends Map<String> anymore and get a compile time error now.

If generics are used Groovy needs to produce java5 bytecode. Groovy needs to be build with >java5 for this (only important if you build Groovy by yourself).

Generics+Covariant Return Types

I already covered these two, but I think the combination is worth an extra word. If you have a program like this
class A<T> {
T foo(T t) {1}
}
class B extends A<Long> {
String foo(Long l) {"2"}
}
then Groovy will do as Java and throw a compile time error. If you use raw types you will see that the following script for example works:
class A<T> {
T foo(T t) {1}
}
class B extends A {
String foo(Long l) {"2"}
}
def b = new B()
assert b.foo(new Object( )) == 1
assert b.foo((Long) 1) == "2"
If used with raw types the method A#foo returns Object and takes Object. Since B#foo takes a Long Groovy will not care about the covariant return types and compile it. So you end up with two version of foo in B, one derived from A returning 1 and one from B returning 2. If the usage is like in the first case above, then A<Long> "kind of" defines a foo method taking a Long and returning a Long. I say "kind of", because the class does not really define that method, it is just the view B gets of A. Anyway, since B defines also a foo(Long) the covariant return type function kicks in, but finds that the return types String and Long are incompatible. So it will give a compile time error. The following script would for example compile:
class A<T> {
T foo(T t) {1}
}
class B extends A<Llon>> {
Long foo(Long l) {2}
}
def b = new B()
assert b.foo((Long) 1) == 2
The foo defined in B will now override the foo defined in A.

Enums

Groovy now supports with 1.1-rc-1 also simple enums. With simple enums I mean you can declare them in Groovy, you can even declare them inside a nnormal class as long as you don't use references from the surrounding class. You can also define methods/fields/properties that will be in all enums. This should cover almost all of the features you need for enums. What we do not support is writing code that is special to one enum value. For example overwritng a method in the enum value, or addding a method/field/property. I have never seen such enums in real live, so I think it is not too important.... On the other hand I haven't seen much enums at all and my picture might be wrong. Anyway, Groovy is most possibly not going to support these enums with additional methods.
Groovy enums will differ a little from Java enums, they will implement GroovyObject and thus you can have a meta class per enum value. That relativates the missing feature with additional methods a bit I think.

If enums are used Groovy needs to produce java5 bytecode.

Asking The User!

I hope all the people out there, that like Groovy so much, will help us in finding bugs for these features. Some features are quite new, sometimes the tests lack fantasy (especially if written at 3 o'clock in then morning) and do not cover all needed cases. Sometimes something is simply misunderstood. I encourage all users to find the bugs and enter issues for them so we can deliver a pleasant 1.1 release to you.

Wednesday, July 18, 2007

About SwingBuilder

Disclamer: I will use the term "closure" quite often here and experts will say they are not closures. I still call them closure in the sense, that they are instances of groovy.lang.Closure. So If I say closure I don't mean that functional thing ;)

This time I thought I should write some things about Groovy SwingBuilder and assumptions people seem to make about it.

groovy.util.BuilderSupport

First thing you need to know is that SwingBuilder is a builder... that might be obvious, but it implies, that if I do a method call in the builder structure, then the builder will handle that call and map the method names to certain actions.

Now in Groovy we have this class BuilderSupport, that you can use to map structures in a builder. Personally I don't like that class much, because the logic looks more complicated than needed, but it fits very general cases. Anyway, the class tries to map method calls in the builder structure to calls of createNode in the builder class. There are several of them, each responsible for a certain case controlled by your method call. The most important fact here is that if your last argument is a closure, then this closure will not be part of the creatNode call, instead the closure will be used by the builder directly. I guess it is best to show examples:

def builder = new MyBuilder()
builder.start {
methodWithClosure {
methodWithMap(foo:"bar")
}
methodWithNormalArgument("I am a argument I guess")
}
methodWithClosure a normal method call with one argument, that is the closure containing the method call with methodWithMap. methodWithClosure is now mapped to createNode(Object), the object there is the method name "methodWithClosure" as String. methodWithMap is mapped to createNode(Object,Map), where the first is again the method name and the map is our [foo:"bar"]. If you combine one normal argument and a map entries, then you get createNode(Object,Map,Object), where the last one contains your normal argument. And if there is no map and no closure, just a normal parameter, like with methodWithNormalArgument, then createNode(Object,Object) will be called. This logic supports only 1(!) normal argument, but that is enough in general.

After the createNode call of your choice is made the return value of that will be hold, I will call this currentNode. To connect the currentNode and its parent, which is done by setParent(currentNode,parentNode). now what is parentNode? Remember? we still have a closure to call. When we do, then our currentNode becomes parentNode and the new currentNode will have a parent. So the first time setParent is called we are not in a closure that belongs to the builder, which means the parentNode is null. If you would build a tree using this logic, then you would build the tree starting with the root and then adding node by node in I think it is called preorder traversal.

Architecture of SwingBuilder

SwingBuilder is making use of these methods in BuilderSupport. For each method call SwingBuilder creates a new instance of a bean we specified with the method call. So
frame(title:"I am a JFrame")
will create a new JFrame instance and set the property title. And as we just learned
frame(title:"I am a JFrame"){
label(text:"I am a JLabel")
}
will also create the JFrame, the property title will be set again, then the closure will be executed causing the label method to create a JLabel and the text property on that label is set. After that setParent is called with the first parameter being the JLabel and the second parameter being the JFrame.

The logic we stored in setParent will connect our frame with the label by frame.getContentPane().add(label). SwingBuilder#setParent knows several cases and handles adding a JMenuBar to a JFrame different from adding a JLable. This method and helper are around 100 lines, about 20% of SwingBuilder source.

So basically SwingBuilder is a builder that maps method calls to bean creation actions, using map arguments to init the beans and the closures to connect the created beans. It using a mapping method name -> bean class and contains itself nearly no methods you call when using SwingBuilder.

Names supported by SwingBuilder

If you are not sure if SwingBuilder supports a swing widget, just remove the J, keep the next letter in lower case and try it. For example JEditorPane becomes editorPane, JSplitPane becomes splitPane (both supported). But SwingBuilder does not only know widgets, it does also know layouts. there is usually no 'J', so just use the next letter in lower case, as in gridBagLayout, flowLayout or others. You can use the layout as normal method causing the layout property of the container to be set. I have often seen code like:
frame(layout:new FlowLayout()) {
label(text:"1")
label(text:"2")
}
but you can write that also as
frame() {
flowLayout()
label(text:"1")
label(text:"2")
}
I like this version much better, because you do not need to import FlowLayout and can give the layout some options while keeping the frame call simple. Groovy supports all the normal layouts, even Box layout. Another special thing is maybe the method gbc, which is the same as the method gridBagCosntraints, which maps to GridBagConstraints. Maybe I should also mention TableLayout, which tries to implement the layout you know from the table tag in html. It needs tr and td calls to place the componentes... really just like in html. Take a look at alpahbetic widget list to get an ideas what you can do.

Another important link is extending SwingBuilder. It does not mention the possibility of simply subclassing the class SwingBuilder, but that should be obvious and was done for example by SwingXBuilder.

All in all it is a bit difficult to provide a documentation for SwingBuilder, because you still need to learn swing, SwingBuilder doesn't help you with that. And then it is just connecting instances of classes... For example when people ask how to attach an action to a JButton and I tell them to assign a closurey to the actionPerformed property, then I am not talking about a special property, actionPerformed is defined by the bean specification and assigning a closure to that property is a normal thing in Groovy.

New things in 1.1-beta2

We got complains that if I do
def frame = swing.frame(...) {
...
}
frame.pack()
frame.visible = true
that the resulting gui will not be constructed in the EDT thread, but in the normal main thread. Now I am no Swing expert and I always assumed it makes no difference, but it seems that future changes in Java will need you to change in the EDT. And of course it is more clean that way too. So we eneded with adding two methods, the first is edt, which causes the attached closure to be executed while in EDT. the code looks then like
swing.edt {
def frame = frame(...) {
...
}
frame.pack()
frame.visible = true
}
unlike many other methods available in SwingBuilder the edt method is a real method and no registered widget. the other method is static and called build. It will automatically create a new SwingBuilder instance and call the attached closure with that instance as parameter
SwingBuilder.build {
def frame = frame(...) {
...
}
frame.pack()
frame.visible = true
}
build uses the edt method, so we build the GUI while in the EDT thread, just like before.

Future Plans

I think SwingBuilder is already a nice piece of work, but its evolution might not stop here. Currently I am thinking about integrating a Binding framework. That would some update logic to SwingBuilder, something you have to do all by yourself atm. For example imagine a label and a button and each time you press the button the label text should show a higher number. What do you do? You use a closure as actionPerformed for your button that increases a number and sets a new text for the label...
import groovy.swing.*
import groovy.swing.impl.*;
import javax.swing.border.EmptyBorder
import javax.swing.WindowConstants

def numClicks = 0
def state = {"Number of button clicks: $numClicks"}
def label
SwingBuilder.build {
def frame = frame (
title: "SwingBuilder Label Update",
defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE
){
panel (border: new EmptyBorder(30, 30, 30, 30)) {
gridLayout(rows: 2, columns: 1, vgap: 10)
button (text: "I'm a button!",
mnemonic: "I",
actionPerformed: {numClicks ++; label.text = state()})
label = label (text: state())
}
}
frame.pack()
frame.visible = true
}
note the closure state, that is called at different places? that's quite ugly I think. With a binding framework the code might become
import groovy.swing.*
import groovy.swing.impl.*;
import javax.swing.border.EmptyBorder
import javax.swing.WindowConstants

def model = new BindModel(numClicks:0)

SwingBuilder.build {
def frame = frame (
title: "Binding and SwingBuilder Test",
defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE
){
panel (border: new EmptyBorder(30, 30, 30, 30)) {
gridLayout(rows: 2, columns: 1, vgap: 10)
button (text: "I'm a button!",
mnemonic: "I",
actionPerformed: {model.numClicks ++})
label (text: bind("Number of button clicks: $model.numClicks"))
}
}
frame.pack()
frame.visible = true
}
as you see the need to keep an external closure vanished along with the need to keep a reference to the label. the way it will be used in the end is not yet sure, this is just a sketch based on a simple implementation I made.

While doing my "research" in this area I noticed that binding frameworks are not that well known. At last by the people I know. I myself didn't here about that before, but I am usually not doing much with swing. So I got a bit puzzled why people don't know these things if they can save so much code... but then I saw it. Some of these frameworks are producing rather cryptic code, that makes sense for the framework but is just plain hard to read. Your former models are now hidden in abstract constructs, but you still need to connect the things. So I guess SwingBuilder could find a more "natural" way by hiding all these things.

We will see.

Wednesday, July 04, 2007

Joint Compilation in Groovy

Note: This is not implemented since today, it is already some weeks old, but there was no information about it on the net... So I wrote this

When working with Languages like Groovy you naturally mix Groovy and Java all the time. But the problem then arises to compile the resulting monster. Especially the Eclipse plugin for Groovy makes you sometimes think your project will compile outside as nice as inside Eclipse. But it might not. Consider for example this case

class A {
B b;
}
class B extends A {}
And think A is written in Groovy, but B is written in Java, or the other way, A is written in Java, but B is written in Groovy. It is clear, that when you compile B, you need class A as well, because the class might be final or abstract or other things that need to be checked. But when you compile A, you will see that it refers B, which means you need to compile B as well. Now if this is one compiler we have no problem, but A and B are written in different languages with compilers not sharing their class information. That means we are stuck. And while this example looks a bit artificial, you get very fast into this situation in a larger project. Who would keep track of not referencing Groovy classes from the Java side to get the compilation done? It's disturbing.

Solutions?

Now the one solution would be to let the compiler share their class data. The Groovy compiler is able to do that, the eclipse compiler is able to do that, we might see a bright future for this in the future. But for example JavaC is not able to do this. At last I don't know how.

Another way is to create stubs for the Groovy classes, run the Java compiler with these stubs, then run the Groovy compiler and overwrite the generated stubs. Alex Tkachman was so free to show us Groovy people how to do this and provided a patch, we could us as base to get this version of the compilation running.

How it Works:

The details of the stub generation are not so important I think, they are created as Java files from the parse tree the Groovy compiler provides in a temporary directory and then feed to JavaC along with the normal Java files. You don't need to start the compiler yourself, the Groovy compiler will do this for you right after the parse phase and then continue with its normal compilation process.

Controlling JavaC:

Since we now have a combined compiler we of course want to use it in our build, but the problem is that javac would by default created a wrong bytecode version for us, we need 1.4 compatible bytecode, so source and target options are needed at last. I then decided to forward the options to the compiler from the command line of Groovy. So if you do
groovyc *.groovy *.java -j -Jsource=1.4 -Jtarget=1.4
You will get the java files compiled for 1.4. -j turns the joint compilation on , the -J parts are gving key-value pairs to the compiler. Using Options without value is also possible using the -F option, just without the equals part like -Fdeprecation. Anything the JavaC compiler supports can be dropped in there... Of course some special options like the VM memory size would not make sense since the VM is already created.

For the GroovyC Ant task the picture is a bit different. first I thought about a way to generically define attributes for the GroovyC task I can forward to JavaC... But me not being the Ant expert I gave this up and decided to do the following work around
<echo message="Groovyc of test code."/>
<java classname="org.codehaus.groovy.ant.Groovyc" fork="true" maxmemory="128M">
<classpath>
<pathelement path="${mainClassesDirectory}"/>
<pathelement path="${testClassesDirectory}"/>
<path refid="testPath"/>
</classpath>
<arg value="${testClassesDirectory}"/>
<arg value="${testSourceDirectory}"/>
<arg value="-j"/>
<arg value="-Jsource=1.4"/>
<arg value="-Jtarget=1.4"/>
</java>
Oh, that reminds me that the GroovyC task needs a fork ability. Anyway, that's when using the ant task from the command line. If you want to use it normally, then
<groovyc
srcdir="${mainSourceDirectory}" destdir="${mainClassesDirectory}"
classpathref="groovyMainCompileDependencies"
jointCompilationOptions="-j -Jsource=1.4 -Jtarget=1.4"
/>
can be used. Same game as on the command line.

What this solution can't do:

Yes, there is a downside. Ok, I think it is already a downside that we have to use a temporary directory, but another one is that we need to know all files we want to compiler before compilation. I don't think that is a problem when running a ant or maven based built, but for the typical usage on the command line, where you just compile your main class and the compiler will get a hold on all further classes will not work. To be more specific, it will not work when the Groovy compiler needs to get an additional Groovy class and the Java compiler would need that class too. That's because in this case no stub will be created and thus the java compiler will fail telling you it can't find a that class. On the other hand GroovyC works with the resulting class files, so if a Groovy class refers a Java class and JavaC did not compile it, then GroovyC won't be able to compile it either... well, ok, just give the compiler all needed files ;)

Future Work:

The current implementation uses JavaC directly a nice framework would be nice here to have more than just this compiler. And there is for example JCI, but JCI seems not to support options... well we need to take another look at it, maybe it supports enough. On the other hand I am thinking about integrating the JavaC task from ant. in that case we could maybe use the normal task as nested element (with some tweaks) and have all the abstraction to different compilers ant allows. Of course by directly using the Eclipse compiler (it is usable outside the IDE) we could let the compiler share class data and then compile files that are not part of the file list given at runtime.

But I think the ant task version will make it. the work around with the "-j -J -F" options might then vanish.


But none the less, have fun with the upcoming
Groovy 1.1 beta 2

Tuesday, March 27, 2007

Visitor Pattern in Groovy

Shame on me, such a long time without update. I was too busy it seems. I just wrote a small article about how to use the visitor pattern in Groovy. Enjoy the Visitor Pattern in Groovy and ignore the misspellings ;)

If you think I should mention other patterns as well, tell me please.