Bean Utils is a support library that aids Fantom-Factory in the development of other libraries, frameworks and applications. Though you are welcome to use it, you may find features are missing and the documentation incomplete.
Bean Utils
is a collection of utilities and software patterns for overcoming common issues associated with data objects.
Features include:
-
Bean Builder Static methods for creating Fantom objects. Don't
make()
your beans,build()
them instead! -
Bean Equality Generate
equals()
,hash()
andtoStr()
methods from fields. -
Bean Properties Get and set object properties and call methods via
property expressions
. -
Type Coercer Convert objects, lists and maps from one type to another using Fantom's standard
toXXX()
andfromXXX()
methods. -
More! Utility methods to find matching ctors and methods.
Bean Utils
is loosely named after JavaBeans,
Install Bean Utils
with the Fantom Pod Manager ( FPM ):
C:\> fpm install afBeanUtils
Or install Bean Utils
with fanr:
C:\> fanr install -r http://eggbox.fantomfactory.org/fanr/ afBeanUtils
To use in a Fantom project, add a dependency to build.fan
:
depends = ["sys 1.0", ..., "afBeanUtils 1.0"]
Full API & fandocs are available on the Eggbox - the Fantom Pod Repository.
Nobody likes writing hash()
and equals()
methods, so let afBeanUtils::BeanEquality take the pain away! Simply annotate important identity fields with @BeanId
and override the Obj methods.
Sample usage:
class User {
Int? id
Str? name
Str? wotever
override Int hash() {
BeanEquality.beanHash(this, [#id, #name])
}
override Bool equals(Obj? obj) {
BeanEquality.beanEquals(this, obj, [#id, #name])
}
override Str toStr() {
BeanEquality.beanToStr(this, [#id, #name])
}
}
afBeanUtils::BeanProperties is a nifty way to get and set properties, and call methods, on nested objects.
Properties are accessed via a property expression. Property expressions look like Fantom code and may traverse many objects. Their main purpose is to get and set properties, but may be used to call methods also.
string := "100 101 102"
BeanProperties.call(string, "split[1].get(2).plus(2).toChar") // --> 3
Using BeanProperties
and a bit of naming convention care, it now becomes trivial to populate an object with properties submitted from a HTTP form:
formBean := BeanProperties.create(MyFormBean#, httpReq.form)
Features of property expressions include:
The simplest use case is getting and setting basic fields. In this example we access the field Buf.capacity
:
buf := Buf()
BeanProperties.get(buf, "capacity") // --> 16
BeanProperties.set(buf, "capacity", 42) // set a new value
When setting fields, the given value is Type Coerced to fit the field type. Consider:
BeanProperties.set(buf, "charset", "UTF-16") // string "UTF-16" is converted to a Charset object
Property expressions can call methods too. Like Fantom code, if the method does not take any parameters then brackets are optional:
buf := Buf()
BeanProperties.call(buf, "flush")
BeanProperties.call(buf, "flush()")
Method arguments may also form part of the expression, and like property values, are type coerced to their respective types:
BeanProperties.call(buf, "fill(255, 4)") // --> 0xFFFFFFFF
BeanProperties.call(buf, "getRange(1..2)") // --> 0xFFFF
Or you may pass arguments in:
BeanProperties.call(buf, "fill", [128, 4]) // --> 0x80808080
BeanProperties.call(buf, "getRange()", [1..2]) // --> 0x8080
Lists, Maps and @Operator
shortcuts for get
and set
may all be traversed using square bracket notation:
list := Str?["a", "b", "c"]
BeanProperties.get(list, "[1]") // --> "b"
All keys and values are Type Coerced to the correct type.
When setting List items special attention is given make sure they don't throw IndexErrs
. Should the list size be smaller than the given index, the list is automatically grown to accommodate:
list := Str?[,]
BeanProperties.set(list, "[1]", "b")
list.size // --> 2
list[0] // --> null
list[1] // --> "b"
If the list items are not nullable, then new objects are created:
list := Str[,]
BeanProperties.set(list, "[1]", "b")
list.size // --> 2
list[0] // --> ""
list[1] // --> "b"
Property expressions become very powerful when chained:
obj.method(arg, arg).map[key].list[idx][operator].field
When traversing a property expression, the last thing you want is a NullErr
half way through. With that in mind, should a property expression encounter null
part way through, a new object is created and set.
Now you can happily chain your expressions with confidence!
If you need more control over when and how intermediate objects are created, then use BeanPropertyFactory
to manually parse property expressions and create your own BeanProperty
instances.