How to verify if an object has certain property? - groovy

I want to use either a value of expected property or a specified default.
How to achieve this in groovy?
Let's look at the example:
def printName(object) {
//if object has initialized property 'name' - print 'name', otherwise print ToString
if (object<some code here>name && object.name) {
print object.name
} else {
print object
}
}

You can use hasProperty. Example:
if (object.hasProperty('name') && object.name) {
println object.name
} else {
println object
}
If you're using a variable for the property name, you can use this:
String propName = 'name'
if (object.hasProperty(propName) && object."$propName") {
...
}

Assuming your object is a Groovy class, you can use hasProperty in the object metaClass like so:
def printName( o ) {
if( o.metaClass.hasProperty( o, 'name' ) && o.name ) {
println "Printing Name : $o.name"
}
else {
println o
}
}
So, then given two classes:
class Named {
String name
int age
String toString() { "toString Named:$name/$age" }
}
class Unnamed {
int age
String toString() { "toString Unnamed:$age" }
}
You can create instance of them, and test:
def a = new Named( name: 'tim', age: 21 )
def b = new Unnamed( age: 32 )
printName( a )
printName( b )
Which should output:
Printing Name : tim
toString Unnamed:32

You can write your own method via meta-programming:
class Foo {
def name = "Mozart"
}
def f = new Foo()
Object.metaClass.getPropertyOrElse = { prop, defaultVal ->
delegate.hasProperty(prop) ? delegate."${prop}" : defaultVal
}
assert "Mozart" == f.getPropertyOrElse("name", "")
assert "Salzburg" == f.getPropertyOrElse("city", "Salzburg")

If I simply want to assert that an object has some property, I just test the following:
assertNotNull(myObject.hasProperty('myProperty').name)
If myObject does not have myProperty the assertion will fail with a null pointer exception:
java.lang.NullPointerException: Cannot get property 'name' on null object

Related

Groovy: writing dynamically compiled class to disc

In my app I need to compile classes / scripts in runtime.
as a Class:
Class<? extends LoginAdapter> clazz = groovyClassLoader.loadClass name
LoginAdapter la = clazz.newInstance id, logo
or as a Closure:
Closure action = groovyShell.evaluate( script, name ) as Closure
Both ways work like charm.
Now I need to be able to write the compiled classes/scripts to some persistant storage (disc) and later restore them back without compiling from scratch.
How can this be done?
For those who might be interested, this is my programming kata:
class Base {
String answer() { null }
String question() { 'WHAT IS.... ?' }
}
class ClazzSpec extends Specification {
def "test compile"() {
given:
String packageName = 'some.pckg'
String body = """
package $packageName
class SomeClass extends Base {
Closure cl = { a -> println a }
#Override String answer(){ '42' }
String json( m ){ JsonOutput.toJson( m ) }
String longOne( int times ){
times.times{ sleep 100 }
'done'
}
}
"""
CompilerConfiguration cc = new CompilerConfiguration( targetDirectory:new File( './out' ) )
ImportCustomizer imp = new ImportCustomizer()
imp.addStarImports 'groovy.json', Base.package.name
cc.addCompilationCustomizers imp, new ASTTransformationCustomizer( value:1, TimedInterrupt )
new Compiler( cc ).compile packageName + '.SomeClass', body
File base = new File( './out' )
GroovyClassLoader gcl = new GroovyClassLoader()
gcl.addClasspath './out'
Class clazz = gcl.loadClass 'some.pckg.SomeClass'
when:
def inst = clazz.newInstance()
then:
inst.question() == 'WHAT IS.... ?'
inst.answer() == '42'
inst.json( [ q:42 ] ) == '{"q":42}'
inst.longOne( 2 ) == 'done'
when:
inst.longOne 11
then:
thrown TimeoutException
}
}

Creation of custom comparator for map in groovy

I have class in groovy
class WhsDBFile {
String name
String path
String svnUrl
String lastRevision
String lastMessage
String lastAuthor
}
and map object
def installFiles = [:]
that filled in loop by
WhsDBFile dbFile = new WhsDBFile()
installFiles[svnDiffStatus.getPath()] = dbFile
now i try to sort this with custom Comparator
Comparator<WhsDBFile> whsDBFileComparator = new Comparator<WhsDBFile>() {
#Override
int compare(WhsDBFile o1, WhsDBFile o2) {
if (FilenameUtils.getBaseName(o1.name) > FilenameUtils.getBaseName(o2.name)) {
return 1
} else if (FilenameUtils.getBaseName(o1.name) > FilenameUtils.getBaseName(o2.name)) {
return -1
}
return 0
}
}
installFiles.sort(whsDBFileComparator);
but get this error java.lang.String cannot be cast to WhsDBFile
Any idea how to fix this? I need to use custom comparator, cause it will be much more complex in the future.
p.s. full source of sample gradle task (description of WhsDBFile class is above):
project.task('sample') << {
def installFiles = [:]
WhsDBFile dbFile = new WhsDBFile()
installFiles['sample_path'] = dbFile
Comparator<WhsDBFile> whsDBFileComparator = new Comparator<WhsDBFile>() {
#Override
int compare(WhsDBFile o1, WhsDBFile o2) {
if (o1.name > o2.name) {
return 1
} else if (o1.name > o2.name) {
return -1
}
return 0
}
}
installFiles.sort(whsDBFileComparator);
}
You can try to sort the entrySet() :
def sortedEntries = installFiles.entrySet().sort { entry1, entry2 ->
entry1.value <=> entry2.value
}
you will have a collection of Map.Entry with this invocation. In order to have a map, you can then collectEntries() the result :
def sortedMap = installFiles.entrySet().sort { entry1, entry2 ->
...
}.collectEntries()
sort can also take a closure as parameter which coerces to a Comparator's compare() method as below. Usage of toUpper() method just mimics the implementation of FilenameUtils.getBaseName().
installFiles.sort { a, b ->
toUpper(a.value.name) <=> toUpper(b.value.name)
}
// Replicating implementation of FilenameUtils.getBaseName()
// This can be customized according to requirement
String toUpper(String a) {
a.toUpperCase()
}

Groovy MetaProgramming - intercept all method even missing ones

I'd like to intercept all method (instance and static) of a class even the missing ones.
Let say that :
class SomeClass {
def methodMissing(String name, args) {
if(name == "unknownMethod"){
return "result from unknownMethod"
}
else throw new MissingMethodException(name, delegate, args)
}
}
SomeClass.metaClass.static.invokeMethod = { methodName, args ->
println "Before"
def result = delegate.metaClass.invokeMethod(delegate, methodName, *args)
println "After"
return result
}
new SomeClass().with{ sc ->
sc.unknownMethod() //throw the MissingMethodExcept
}
This works well for method that are implemented by the class but when it's a method handled by methodMissing, I get a MissingMethodException...
How would you do that?
Thanks in advance
I think you need to catch the non static invokeMethod as well
Also, you need to go through getMetaMethod to call the original method or else you run the risk of stackoverflows.
Given the following:
class SomeClass {
String name
static String joinWithCommas( a, b, c ) {
[ a, b, c ].join( ',' )
}
String joinAfterName( a, b, c ) {
"$name : ${SomeClass.joinWithCommas( a, b, c )}"
}
def methodMissing(String name, args) {
if(name == "unknownMethod"){
return "result from unknownMethod"
}
else {
throw new MissingMethodException( name, SomeClass, args )
}
}
}
// Return a closure for invoke handler for a class
// with a given title (for the logging)
def invokeHandler = { clazz, title ->
{ String methodName, args ->
println "Before $methodName ($title)"
def method = clazz.metaClass.getMetaMethod( methodName, args )
def result = method == null ?
clazz.metaClass.invokeMissingMethod( delegate, methodName, args ) :
method.invoke( delegate, args )
println "After $methodName result = $result"
result
}
}
SomeClass.metaClass.invokeMethod = invokeHandler( SomeClass, 'instance' )
SomeClass.metaClass.static.invokeMethod = invokeHandler( SomeClass, 'static' )
new SomeClass( name:'tim' ).with { sc ->
sc.joinAfterName( 'a', 'b', 'c' )
sc.unknownMethod( 'woo', 'yay' )
sc.cheese( 'balls' )
}
I get the output:
Before with (instance)
Before joinAfterName (instance)
Before joinWithCommas (static)
After joinWithCommas result = a,b,c
After joinAfterName result = tim : a,b,c
Before unknownMethod (instance)
After unknownMethod result = result from unknownMethod
Before cheese (instance)
Exception thrown
groovy.lang.MissingMethodException: No signature of method: SomeClass.cheese() is applicable for argument types: (java.lang.String) values: [balls]

groovy change bean variables using properties

Is there a way in groovy to do something like:
class Person{
def name, surname
}
public void aMethod(anoherBean){
def bean = retrieveMyBean()
p.properties = anoherBean.properties
}
The property properties is final, is there another way to do this shortcut?
properties is a virtual property; you have to call the individual setters. Try this:
def values = [name: 'John', surname: 'Lennon']
for( def entry : values.entries() ) {
p.setProperty( entry.getKey(), entry.getValue() );
}
Or, using MOP:
Object.class.putAllProperties = { values ->
for( def entry : values.entries() ) {
p.setProperty( entry.getKey(), entry.getValue() );
}
}
Person p = new Person();
p.putAllProperties [name: 'John', surname: 'Lennon']
[EDIT] To achieve what you want, you must loop over the properties. This blog post describes how to do that:
def copyProperties(def source, def target){
target.metaClass.properties.each{
if (source.metaClass.hasProperty(source, it.name) && it.name != 'metaClass' && it.name != 'class')
it.setProperty(target, source.metaClass.getProperty(source, it.name))
}
}
If you don't have any special reason then just use named parameters
def p = new Person(name: 'John', surname: 'Lennon')
After question being updated
static copyProperties(from, to) {
from.properties.each { key, value ->
if (to.hasProperty(key) && !(key in ['class', 'metaClass']))
to[key] = value
}
}

Dynamically add a property or method to an object in groovy

Is it possible to add a property or a method to an object dynamically in Groovy? This is what I have tried so far:
class Greet {
def name
Greet(who) { name = who[0].toUpperCase() + [1..-1] }
def salute() { println "Hello $name!" }
}
g = new Greet('world') // create object
g.salute() // Output "Hello World!"
g.bye = { println "Goodbye, $name" }
g.bye()
But I get the following exception:
Hello World!
Caught: groovy.lang.MissingPropertyException: No such property: bye for class: Greet
Possible solutions: name
at test.run(greet.groovy:11)
If you just want to add the bye() method to the single instance g of the class Greet, you need to do:
g.metaClass.bye = { println "Goodbye, $name" }
g.bye()
Otherwise, to add bye() to all instance of Greet (from now on), call
Greet.metaClass.bye = { println "Goodbye, $name" }
But you'd need to do this before you create an instance of the Greet class
Here is a page on the per-instance metaClass
And here is the page on MetaClasses in general
Also, there's a bug in your constructor. You're missing who from infront of your [1..-1] and if the constructor is passed a String of less than 2 characters in length, it will throw an exception
A better version might be:
Greet( String who ) {
name = who.inject( '' ) { String s, String c ->
s += s ? c.toLowerCase() : c.toUpperCase()
}
}
As metioned in the comments,
Greet( String who ) {
name = who.capitalize()
}
is the proper way

Resources