| Things to Test |
| ============== |
| |
| * test synchronized statement |
| |
| * test compiler errors when |
| - duplicate methods testno |
| - duplicate class in module |
| |
| * test Button(actionPerformed:{event| event.println() } |
| |
| |
| Core Groovy Tasks |
| ================= |
| |
| * allow maps to be created using identifiers? |
| {name:'James', address:'London'} |
| |
| only issue is the identifiers could conflict. |
| |
| Could use a helper method? |
| |
| * handling sequences (0-n, 1-n) |
| they are Lists & behave like lists, any methods on the items will |
| be called etc. |
| |
| however how are they assigned / modified. |
| |
| foo.customers += customer |
| |
| foo.customers = [customer1, customer2] |
| |
| so assignment of sequence fields don't really do an assign; they do a set(List) instead |
| |
| - should we allow real assignments. e.g. |
| |
| Address* foo = ...; |
| foo = [1, 2, 3] |
| |
| foo keeps its type so its not a List |
| |
| how about handling mandatory values - |
| the set we overload to do the non-null checks? |
| this must be a type. e.g. |
| |
| String! x = "hello" |
| x = null // error |
| |
| * new GroovyMethods to be added |
| - File.grep(pattern) -> List |
| - File.grep(pattern) { closure } |
| allow iteration through a file for each line matching a regex pattern |
| - Collection.grep(pattern) -> List |
| - Collection.grep(pattern) { closure } |
| where pattern = regexp (could maybe be other kinds of patterns like Range?) |
| |
| - List.first(), List.last(), pop() |
| - Collection.removeIf { x | x > 0 } |
| - List.eachWithindex { item, idx | ... } |
| - List.reverseEach { ... } |
| - Collection.count(foo) -> returns number of objects that equal foo |
| - Collection.countIf { it > 5 } -> returns number of objects that match closure predicate |
| |
| - Map.get(key, defaultValue) |
| - Map.setDefault(key, defaultValue) for things like |
| map.setDefault(key, []).add(newValue) |
| |
| - Object.getMetaClass() |
| - Object.eachProperty |
| - Object.eachPropertyName { object.getProperty(it) } |
| - Object.setProperties(foo:'abc', x:123) |
| |
| - Map.eachKey, eachValue |
| - Map.index, Map.indexes |
| - configure method on beans? |
| bean.setProperties(name:'James', location:'London') |
| - some kind of Regexp iterator of strings like Ruby's scan |
| - maybe support Pythons' zip and reduce functions? |
| |
| maybe add the various readonly List APIs to Object[] and *[] types |
| if we ever support the DTD / Xed style type*, type+ then we can do the same |
| there too |
| |
| * add doesNotUnderstand() method for catching & handling unknown methods |
| |
| * is there a better way to add listeners in Groovy. e.g. how to remove added listeners |
| |
| listener = bean.property + { some closure } |
| bean.property - listener |
| |
| * BeanBuilder - builder which creates a tree of beans |
| builder.bean(class:SomeClass, x:123, b:'hello') { |
| nestedProperty(a:123) |
| nestedProperty2(class:DerivedClass, b:43) |
| } |
| using adder method as well as setter methods |
| |
| * MBeanBuilder? |
| builder.mlet { |
| mbean(class:SomeClass) { |
| someAttribute(value) |
| } |
| mbean(class:SomeClass, someAttribute:value) { |
| operation(1, 2) |
| } |
| } |
| |
| * templating - allow looping & conditionals? |
| |
| text = " |
| <table> |
| #for (f in foo) |
| <tr> |
| <td>${f.name} |
| </tr> |
| #end |
| </table> |
| " |
| |
| * allow if/switch to be the and of a function. i.e. |
| foo() { |
| if (x > 1) { |
| "hello" |
| } |
| else { |
| "cheese" |
| } |
| } |
| ditto for switch? i.e. make return statement optional. |
| |
| |
| * SQL builder |
| |
| sql.table("users") { |
| ÊÊÊ column('user-id') { autoinc(), primaryKey(), references('foo'), initialValue(1234) } |
| ÊÊÊ column('nickname') { varchar(200) } |
| column(name:'foo', type:'varchar(200)', references:['a', 'b'], initialValue:1234) |
| } |
| } |
| |
| * using mixin for adding using style behaviour to any object?? |
| |
| mixin Using { |
| |
| static using(yield) { |
| object = this.newInstance() |
| try { |
| yield(object) |
| object.close() |
| } |
| catch (Exception e) { |
| try { |
| object.close() |
| } |
| catch (Exception e2) |
| // ignore e2 |
| throw e |
| } |
| } |
| } |
| |
| or |
| |
| using(yield) { |
| try { |
| yield(this) |
| close() |
| } |
| catch (Exception e) { |
| try { |
| close() |
| } |
| catch (Exception e2) |
| // ignore e2 |
| throw e |
| } |
| } |
| } |
| } |
| |
| then use it as |
| |
| new FileInputStream().using { in | |
| ... |
| } |
| |
| * looks like a bug on generated methods, should use param name over any field name |
| - also it looks like there's various unnecessary stuff (creation of tuples) when invoking |
| methods |
| |
| * groovy thread... |
| foo = new GroovyThread(args) { closure } |
| |
| * MetaClass -> add getPropertyDescriptor which has methods |
| - get(), set(), type(), cardinality() etc |
| |
| could use naming conventions to detect cardinality - e.g. addFoo(), getFoos() |
| |
| * generate property change listener stuff on properties |
| |
| * test identity -> hashCode + equals methods |
| |
| * support for property converters, based on type |
| |
| * go through all not-working @todo tests in src/test |
| |
| * to support dynamic mixins we should use dynamic proxies to allow |
| a groovy object to change metaclass at runtime |
| |
| * groovy dynamic proxy of any Java object in Java-land? |
| NullObject pattern |
| |
| * immutable bean |
| |
| * MetaClass.deriveMetaClass() |
| MetaClass.addMixin(mixin) |
| |
| * constructors with map / tuple expressions, named parameter calling |
| |
| * default values for parameters |
| |
| * test try / catch with multiple exception types |
| |
| * * support static newInstance() method for constructors |
| |
| * maybe split up ClassGenerator - do code gen & class gen separately |
| |
| * mixin support... |
| |
| SomeClass.addMixin(Foo); |
| |
| MetaClass.addInterceptor( new Interceptor() { |
| filter(method) { |
| return method.isPublic(); |
| } |
| invoke(method, args) { |
| // do something |
| method.invoke(args); |
| } |
| }); |
| |
| * allow meta classes to be added dynamically using closure syntax? |
| e.g. Map? |
| |
| |
| STUFF TO PONDER |
| =============== |
| |
| * if we allow statements in class declarations, then we could disallow the syntax sugar |
| for method invocation with closures. i.e. foo { code } would have to be foo ({code}) |
| otherwise it'd be interpeted as a method declaration. |
| |
| e.g. |
| |
| module MyValidatingModule |
| |
| class Cheese { |
| readOnlyProperty('hello') |
| validatingProperty('name', { it.length > 0 } ) |
| |
| lazilyCreatedProperty(:cheese, { new MySpecialCheese() }) |
| |
| public(:methodA, :methodB, :methodC) |
| } |
| |
| for now we could argue that only methods are allowed to be invoked inside the class declaration. |
| We could still do markup but just not as nice |
| |
| class Whatnot { |
| table( { |
| foo { |
| } |
| whatnot { |
| } |
| }) |
| |
| blah = { |
| a(x:1234, b:942) { |
| d() |
| } |
| } |
| |
| |
| * Support multiple return values... |
| |
| String, Number cheese() { |
| "hello", 7 |
| } |
| |
| a, b = cheese() |
| |
| also if we do this we should do assignment / swapping |
| |
| a, b = 1, 2 |
| a, b = b, a |
| |
| * maybe use syntax #[1, 2] to create arrays which behave just like immutable lists? |
| |
| * using macros to avoid dependencies on logging stuff (say) |
| |
| class GroovyLog { |
| switch (System.getProperty('groovy.log.impl', 'useCommonsLogging')) { |
| case 'useCommonsLogging': { |
| // lets define the new instance method |
| newInstance() { |
| return new CommonsLoggingThingy() |
| } |
| } |
| default { |
| newInstance() { |
| return new SimpleGroovyLog() |
| } |
| } |
| } |
| } |
| |
| doing things like this at compile time means no runtime dependencies. Ditto to do JDK based compiles |
| |
| |
| |
| UI WORK |
| ======= |
| |
| * tree demo... |
| |
| * when named method calls are supported with default values, refactor SwingBuilder |
| so that all the creations of widgets occur with SwingFactory which would be |
| useful in and of itself |
| - plus we should be using normal method call mechanism & for groovy to do the rest to avoid |
| the long laborious Map coding |
| |
| * add table layout cell spacing thingy |
| |
| * FormModel.addPropertyModel(property) |
| FormModel.addClosureModel(readClosure, writeClosure) |
| |
| * ListModel is-a List but delegates to an underlying list and has events |
| |
| * rename tableLayout -> table and table -> grid |
| |
| * add sortableGrid |
| |
| * create a GroovyUI |
| -> interactive script + allows scripts to be run & objects explored |
| |
| |
| |
| |
| ECLIPSE WORK |
| ============ |
| |
| * Run as JUnit support for Groovy! |
| |
| * Refactoring support for Groovy and tie it into the Java refactoring |
| |
| |
| JUNIT WORK |
| ========== |
| |
| * patch GroovyTestCase so that methods which return Object are included in the test. This avoids us having to |
| specify void for method return types.This requires a clever static method when we generate the |
| bytecode which can instantiate a special kind of TestSuite |
| unless there's another way? |
| |
| |
| OPTIMISATIONS |
| ============= |
| * method invocations - if foo instanceof GroovyObject |
| then generate bytecode |
| |
| foo.invokeMethod(method, args); |
| |
| * could code generate the MetaClass with very efficient dynamic dispatch |
| e.g. could switch() on the method name & then use real method invocation |
| on the method instance |