This repository has been archived by the owner on Sep 17, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExtendedDataBindingGrailsPlugin.groovy
153 lines (136 loc) · 5.94 KB
/
ExtendedDataBindingGrailsPlugin.groovy
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
import org.codehaus.groovy.grails.web.binding.GrailsDataBinder
import org.springframework.beans.BeanWrapperImpl
import org.springframework.validation.DataBinder
import org.springframework.validation.BindingResult
import br.inf.freeit.extendeddatabinding.WrappedBean
class ExtendedDataBindingGrailsPlugin {
def version = "0.5-M1"
def observe = ['controllers']
// the version or versions of Grails the plugin is designed for
def grailsVersion = "1.3.0 > *"
// the other plugins this plugin depends on
def dependsOn = [:]
// resources that are excluded from plugin packaging
def pluginExcludes = [
"grails-app/conf",
"grails-app/views",
"web-app/*"
]
def author = "Luis Fernando Planella Gonzalez, Bernardo Gomez Palacio"
def authorEmail = "[email protected]"
def title = "This plugin extends Grails' data binding possibilities, both for parsing the user input and to present formatted data"
def description = '''\
The extended data binding plugin aims to allow configuring the DataBinder which controllers will use to parse
the user input and populate objects, as well as using a custom class, WrappedBean to format data as String.
This configuration can be done application-wide by setting closures on the servlet context
(possibly on the ApplicationBootStrap) on the following attributes: newDataBinder (takes the request and the
object and should return a GrailsDataBinder instance) and newBeanWrapper (also takes the request and the
object and should return a Spring's BeanWrapper instance).
This plugin adds some methods to controllers: getBinder (takes an object and returns a DataBinder for that object, setting
it on the request under the attribute 'dataBinder'), wrapBean (takes an object and returns a WrappedBean instance -
a class that uses an Spring's BeanWrapper and converts properties to strings using registered PropertyEditors) and bind
(takes an object, performs the data binding and returns the object itself). Both getBinder and wrapBean methods invoke
the global newDataBinder or newBeanWrapper closures (as explained above) and try calling a registerCustomEditors method
(passing the object) on the controller, which may be used to set any specific PropertyEditors on either DataBinder or
BeanWrapper). The getBinder also tries caling a initBinder method on the controller after registerCustomEditors.
Also, a tag library adds the following tags to the 'g' namespace: wrap (takes a bean attribute and a var attribute,
exporting a WrappedBean instance into the given var - optionally under a custom scope) and eachWrapped (same as g:each,
but automatically wrapping each element)
'''
def documentation = "http://www.grails.org/plugin/extended-data-binding"
private boolean loggedDataBinder = false
private boolean loggedBeanWrapper = false
def doWithSpring = {
}
def doWithApplicationContext = { applicationContext ->
}
def doWithWebDescriptor = { xml ->
}
def doWithDynamicMethods = { final ctx ->
application.controllerClasses.each { controllerClass ->
addMethods(ctx, controllerClass)
}
}
def onChange = { event ->
if (event.application.isControllerClass(event.source)) {
addMethods(event.applicationContext, event.source)
}
}
def onApplicationChange = { event ->
}
def addMethods = { final ctx, final controllerClass ->
// Binds the current request to the given object, returning the object itself
controllerClass.metaClass.bind = { object, Map args = [:] ->
if (object == null) return null
DataBinder dataBinder = getBinder(object)
if (args?.include && args.include.size() > 0) {
dataBinder.setAllowedFields(args?.include as String[] )
}
if (args?.exclude && args.exclude.size() > 0) {
dataBinder.setDisallowedFields(args?.exclude as String[])
}
dataBinder.bind(params)
BindingResult result = dataBinder.bindingResult
try {
object.setErrors(result)
} catch (Exception e) {
//Ignore
}
object
}
// The getDataBinder method will return the DataBinder instance, storing it on the request on the 'dataBinder' attribute
controllerClass.metaClass.getBinder = { object ->
def binder = null
try {
def closure = ctx.servletContext.getAttribute('newDataBinder')
if (closure != null) {
binder = closure(request, object)
}
} catch (Exception ex) {
if (!loggedDataBinder) {
log.warn("Error invoking closure under servletContext['newDataBinder']", ex)
}
}
if (!(binder instanceof GrailsDataBinder)) {
if (!loggedDataBinder) {
log.info("No global configuration for DataBinders. Assuming defaults.")
loggedDataBinder = true
}
binder = GrailsDataBinder.createBinder(object, GrailsDataBinder.DEFAULT_OBJECT_NAME, request)
}
try {
registerCustomEditors(binder)
} catch (Exception ex) {}
try {
initBinder(binder)
} catch (Exception ex) {}
request.setAttribute("dataBinder", binder)
return binder
}
// Wraps the given bean on a BeanWrapper
controllerClass.metaClass.wrapBean = { object ->
if (object instanceof WrappedBean) return object
def beanWrapper = null
try {
def closure = ctx.servletContext.getAttribute('newBeanWrapper')
beanWrapper = closure(request, object)
} catch (Exception ex) {
if (!loggedBeanWrapper) {
log.warn("Error invoking closure under servletContext['newBeanWrapper']", ex)
}
}
if (!beanWrapper) {
if (!loggedBeanWrapper) {
log.info("No global configuration for BeanWrappers. Assuming defaults.")
loggedBeanWrapper = true
}
beanWrapper = new BeanWrapperImpl()
}
beanWrapper.setWrappedInstance(object)
try {
registerCustomEditors(beanWrapper)
} catch (Exception ex) {}
return new WrappedBean(beanWrapper)
}
}
}