Skip to content

Commit b797d51

Browse files
author
leebutts
committed
GRAILS-998
git-svn-id: https://svn.codehaus.org/grails/trunk@4677 1cfb16fd-6d17-0410-8ff1-b7e8e1e2867d
1 parent e1e68ce commit b797d51

File tree

6 files changed

+175
-24
lines changed

6 files changed

+175
-24
lines changed

src/groovy/org/codehaus/groovy/grails/plugins/web/ControllersGrailsPlugin.groovy

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,10 +383,16 @@ class ControllersGrailsPlugin {
383383
bind.invoke(delegate, "bindData", [target, args] as Object[])
384384
}
385385
metaClass.bindData = { Object target, Object args, List disallowed ->
386-
bind.invoke(delegate, "bindData", [target, args, disallowed] as Object[])
386+
bind.invoke(delegate, "bindData", [target, args, [exclude:disallowed]] as Object[])
387387
}
388388
metaClass.bindData = { Object target, Object args, List disallowed, String filter ->
389-
bind.invoke(delegate, "bindData", [target, args, disallowed, filter] as Object[])
389+
bind.invoke(delegate, "bindData", [target, args, [exclude:disallowed] , filter] as Object[])
390+
}
391+
metaClass.bindData = { Object target, Object args, Map includeExclude ->
392+
bind.invoke(delegate, "bindData", [target, args, includeExclude] as Object[])
393+
}
394+
metaClass.bindData = { Object target, Object args, Map includeExclude, String filter ->
395+
bind.invoke(delegate, "bindData", [target, args, includeExclude , filter] as Object[])
390396
}
391397
metaClass.bindData = { Object target, Object args, String filter ->
392398
bind.invoke(delegate, "bindData", [target, args, filter] as Object[])

src/web/org/codehaus/groovy/grails/web/binding/GrailsDataBinder.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public class GrailsDataBinder extends ServletRequestDataBinder {
7070
public static final String[] GROOVY_DOMAINCLASS_DISALLOWED = new String[] { "metaClass", "properties", "id", "version" };
7171
public static final String NULL_ASSOCIATION = "null";
7272
private static final String PREFIX_SEPERATOR = ".";
73+
private static final String[] ALL_OTHER_FIELDS_ALLOWED_BY_DEFAULT = new String[0];
7374

7475
/**
7576
* Create a new GrailsDataBinder instance.
@@ -82,7 +83,7 @@ public GrailsDataBinder(Object target, String objectName) {
8283

8384
bean = ((BeanPropertyBindingResult)super.getBindingResult()).getPropertyAccessor();
8485

85-
String[] disallowed = null;
86+
String[] disallowed = new String[0];
8687
GrailsApplication grailsApplication = ApplicationHolder.getApplication();
8788
if (grailsApplication!=null && grailsApplication.isArtefactOfType(DomainClassArtefactHandler.TYPE, target.getClass())) {
8889
if (target instanceof GroovyObject) {
@@ -93,9 +94,8 @@ public GrailsDataBinder(Object target, String objectName) {
9394
} else if (target instanceof GroovyObject) {
9495
disallowed = GROOVY_DISALLOWED;
9596
}
96-
if (disallowed != null) {
97-
setDisallowedFields(disallowed);
98-
}
97+
setDisallowedFields(disallowed);
98+
setAllowedFields(ALL_OTHER_FIELDS_ALLOWED_BY_DEFAULT);
9999
}
100100

101101
/**

src/web/org/codehaus/groovy/grails/web/metaclass/BindDynamicMethod.java

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.ArrayList;
2929
import java.util.List;
3030
import java.util.Map;
31+
import java.util.HashMap;
3132
import java.util.regex.Pattern;
3233

3334
/**
@@ -44,6 +45,8 @@
4445
public class BindDynamicMethod extends AbstractDynamicMethodInvocation {
4546
public static final String METHOD_SIGNATURE = "bindData";
4647
public static final Pattern METHOD_PATTERN = Pattern.compile('^'+METHOD_SIGNATURE+'$');
48+
private static final String INCLUDE_MAP_KEY = "include";
49+
private static final String EXCLUDE_MAP_KEY = "exclude";
4750

4851
public BindDynamicMethod() {
4952
super(METHOD_PATTERN);
@@ -58,60 +61,98 @@ public Object invoke(Object target, String methodName, Object[] arguments) {
5861

5962
Object targetObject = arguments[0];
6063
Object bindParams = arguments[1];
61-
List disallowed = null;
64+
Map includeExclude = new HashMap();
65+
List include = null;
66+
List exclude = null;
6267
String filter = null;
6368
switch(arguments.length){
6469
case 3:
6570
if(arguments[2] instanceof String){
6671
filter = (String) arguments[2];
67-
}else if(!(arguments[2] instanceof List)) {
68-
throw new IllegalArgumentException("The 3rd Argument for method bindData must represent disallowed properties " +
69-
"and implement the interface java.util.List or be a String and represent a prefix to filter parameters with");
72+
}else if(!(arguments[2] instanceof Map)) {
73+
throw new IllegalArgumentException("The 3rd Argument for method bindData must represent included and exlucded properties " +
74+
"and implement the interface java.util.Map or be a String and represent a prefix to filter parameters with");
7075
}else {
71-
disallowed = (List) arguments[2];
76+
includeExclude = (Map) arguments[2];
7277
}
7378
break;
7479
case 4:
75-
if(!( arguments[2] instanceof List)) {
76-
throw new IllegalArgumentException("Argument [disallowed] for method [bindData] must implement the interface [java.util.List]");
80+
if(!( arguments[2] instanceof Map)) {
81+
throw new IllegalArgumentException("The 3rd Argument for method bindData must represent included and exlucded properties " +
82+
"and implement the interface java.util.Map or be a String and represent a prefix to filter parameters with");
7783
}
78-
disallowed = (List) arguments[2];
84+
includeExclude = (Map) arguments[2];
7985
if(!(arguments[3] instanceof String)) {
8086
throw new IllegalArgumentException("Argument [prefix] for method [bindData] must be a String");
8187
}
8288
filter = (String) arguments[3];
8389
break;
8490
}
8591

92+
if(includeExclude.containsKey(INCLUDE_MAP_KEY)){
93+
Object o = includeExclude.get(INCLUDE_MAP_KEY);
94+
include = convertToListIfString(o);
95+
}
96+
97+
if(includeExclude.containsKey(EXCLUDE_MAP_KEY)){
98+
Object o = includeExclude.get(EXCLUDE_MAP_KEY);
99+
exclude = convertToListIfString(o);
100+
}
101+
86102
GrailsDataBinder dataBinder;
87103
if(bindParams instanceof GrailsParameterMap) {
88104
GrailsParameterMap parameterMap = (GrailsParameterMap)bindParams;
89105
HttpServletRequest request = parameterMap.getRequest();
90106
dataBinder = GrailsDataBinder.createBinder(targetObject, targetObject.getClass().getName(), request);
91-
updateDisallowed( dataBinder, disallowed);
107+
includeExcludeFields(dataBinder, include, exclude);
92108
dataBinder.bind(request, filter);
93109
}
94110
else if(bindParams instanceof HttpServletRequest) {
95111
GrailsWebRequest webRequest = (GrailsWebRequest)RequestContextHolder.currentRequestAttributes();
96112
dataBinder = GrailsDataBinder.createBinder(targetObject, targetObject.getClass().getName(),webRequest.getCurrentRequest());
97-
updateDisallowed( dataBinder, disallowed);
113+
includeExcludeFields(dataBinder, include, exclude);
98114
dataBinder.bind((HttpServletRequest)bindParams, filter);
99115
}
100116
else if(bindParams instanceof Map) {
101117
dataBinder = new GrailsDataBinder(targetObject, targetObject.getClass().getName());
102118
PropertyValues pv = new MutablePropertyValues((Map)bindParams);
103-
updateDisallowed( dataBinder, disallowed);
119+
includeExcludeFields(dataBinder, include, exclude);
104120
dataBinder.bind(pv, filter);
105121
}
106122
else {
107123
GrailsWebRequest webRequest = (GrailsWebRequest)RequestContextHolder.currentRequestAttributes();
108124
dataBinder = GrailsDataBinder.createBinder(targetObject, targetObject.getClass().getName(), webRequest.getCurrentRequest());
109-
updateDisallowed( dataBinder, disallowed);
125+
includeExcludeFields(dataBinder, include, exclude);
110126
dataBinder.bind(webRequest.getCurrentRequest(), filter);
111127
}
112128
return targetObject;
113129
}
114130

131+
private List convertToListIfString(Object o) {
132+
if(o instanceof String){
133+
List list = new ArrayList();
134+
list.add(o);
135+
o = list;
136+
}
137+
return (List) o;
138+
}
139+
140+
private void includeExcludeFields(GrailsDataBinder dataBinder, List allowed, List disallowed) {
141+
updateAllowed( dataBinder, allowed);
142+
updateDisallowed( dataBinder, disallowed);
143+
}
144+
145+
private void updateAllowed(GrailsDataBinder binder, List allowed) {
146+
if (allowed != null) {
147+
String[] currentAllowed = binder.getAllowedFields();
148+
List newAllowed = new ArrayList(allowed);
149+
CollectionUtils.addAll( newAllowed, currentAllowed);
150+
String[] value = new String[newAllowed.size()];
151+
newAllowed.toArray(value);
152+
binder.setAllowedFields(value);
153+
}
154+
}
155+
115156
private void updateDisallowed( GrailsDataBinder binder, List disallowed) {
116157
if (disallowed != null) {
117158
String[] currentDisallowed = binder.getDisallowedFields();

test/groovy/org/codehaus/groovy/grails/plugins/web/ControllersGrailsPluginTests.groovy

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,45 @@ class TestController {
2929
assert appCtx.containsBean("TestControllerClass")
3030
assert appCtx.containsBean("TestController")
3131
}
32+
33+
void testOldBindDataMethodsDelegateToNewOnes() {
34+
Class testClass = parseTestBean()
35+
def controller = appCtx.getBean("TestController")
36+
def bean = testClass.newInstance()
37+
def params = [name:"beanName", pages:3]
38+
controller.bindData(bean, params, ["pages"])
39+
assertEquals(0, bean.pages)
40+
assertEquals("beanName", bean.name)
41+
42+
bean = testClass.newInstance()
43+
params = ['a.name':"beanName", 'b.address':"address", 'a.pages':3]
44+
controller.bindData(bean, params, ["pages"], "a")
45+
assertEquals(0, bean.pages)
46+
assertEquals("beanName", bean.name)
47+
assertNull(bean.address)
48+
49+
}
50+
51+
void testBindDataConvertsSingleIncludeToListInternally() {
52+
Class testClass = parseTestBean()
53+
def bean = testClass.newInstance()
54+
def params = ['a.name':"beanName", 'b.address':"address", 'a.pages':3]
55+
def controller = appCtx.getBean("TestController")
56+
controller.bindData(bean, params, [include:"name"], "a")
57+
assertEquals(0, bean.pages)
58+
assertEquals("beanName", bean.name)
59+
assertNull(bean.address)
60+
}
61+
62+
Class parseTestBean(){
63+
return gcl.parseClass(
64+
"""
65+
class TestDomainObject {
66+
String name
67+
int pages = 0
68+
String address
69+
}
70+
""")
71+
}
72+
3273
}

test/groovy/org/codehaus/groovy/grails/web/servlet/BindDataMethodTests.groovy

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ class BindDataMethodTests extends AbstractGrailsControllerTests {
4343
}
4444
}
4545

46-
void testBindDataWithDisallowed() {
46+
void testBindDataWithExcluded() {
4747
runTest() {
4848
mockController = ga.getControllerClass("BindController")
4949
def src = [ 'metaClass' : this.metaClass, 'name' : 'Marc Palmer', 'email' : 'dontwantthis' ]
50-
def excludes = ['email']
50+
def excludes = [exclude:['email']]
5151

5252
method.invoke( mockController,"bindData", [target, src, excludes].toArray() )
5353

@@ -57,6 +57,46 @@ class BindDataMethodTests extends AbstractGrailsControllerTests {
5757
}
5858
}
5959

60+
void testBindDataWithIncluded() {
61+
runTest() {
62+
mockController = ga.getControllerClass("BindController")
63+
def src = [ 'metaClass' : this.metaClass, 'name' : 'Marc Palmer', 'email' : 'dontwantthis' ]
64+
def includes = [include:['name']]
65+
66+
method.invoke( mockController,"bindData", [target, src, includes].toArray() )
67+
68+
assertEquals "Marc Palmer", target.name
69+
assertEquals safeMeta, target.metaClass
70+
assertNull target.email
71+
}
72+
}
73+
74+
void testBindDataWithNeitherIncludeOrExcludeIncludesAll() {
75+
runTest() {
76+
mockController = ga.getControllerClass("BindController")
77+
def src = [ 'metaClass' : this.metaClass, 'name' : 'Marc Palmer', 'email' : 'dowantthis' ]
78+
method.invoke( mockController,"bindData", [target, src, [:]].toArray() )
79+
80+
assertEquals "Marc Palmer", target.name
81+
assertEquals safeMeta, target.metaClass
82+
assertEquals target.email, 'dowantthis'
83+
}
84+
}
85+
86+
void testBindDataExcludedOverridesIncluded() {
87+
runTest() {
88+
mockController = ga.getControllerClass("BindController")
89+
def src = [ 'metaClass' : this.metaClass, 'name' : 'Marc Palmer', 'email' : 'dontwantthis' ]
90+
def includedAndExcluded = [include:['name','email'], exclude:['email']]
91+
92+
method.invoke( mockController,"bindData", [target, src, includedAndExcluded].toArray() )
93+
94+
assertEquals "Marc Palmer", target.name
95+
assertEquals safeMeta, target.metaClass
96+
assertNull target.email
97+
}
98+
}
99+
60100
void testBindDataWithPrefixFilter() {
61101
runTest() {
62102
mockController = ga.getControllerClass("BindController")
@@ -78,7 +118,7 @@ class BindDataMethodTests extends AbstractGrailsControllerTests {
78118
input.each() {
79119
webRequest.currentRequest.addParameter((String)it.key, (String)it.value)
80120
}
81-
def excludes = ['email']
121+
def excludes = [exclude:['email']]
82122
def params = new GrailsParameterMap(webRequest.currentRequest)
83123

84124
method.invoke( mockController,"bindData", [target, params, excludes].toArray() )
@@ -96,7 +136,21 @@ class BindDataMethodTests extends AbstractGrailsControllerTests {
96136
def src = [ 'metaClass' : this.metaClass, 'mark.name' : 'Marc Palmer', 'mark.email' : 'dontwantthis',
97137
'lee.name': 'Lee Butts', 'lee.email': '[email protected]']
98138
def filter = "lee"
99-
def disallowed = ["email"]
139+
def disallowed = [exclude:["email"]]
140+
method.invoke( mockController,"bindData", [target, src, disallowed, filter].toArray() )
141+
assertEquals "Lee Butts", target.name
142+
assertNull target.email
143+
assertEquals safeMeta, target.metaClass
144+
}
145+
}
146+
147+
void testBindDataConvertsSingleStringInMapToList() {
148+
runTest() {
149+
mockController = ga.getControllerClass("BindController")
150+
def src = [ 'metaClass' : this.metaClass, 'mark.name' : 'Marc Palmer', 'mark.email' : 'dontwantthis',
151+
'lee.name': 'Lee Butts', 'lee.email': '[email protected]']
152+
def filter = "lee"
153+
def disallowed = [exclude:"email"]
100154
method.invoke( mockController,"bindData", [target, src, disallowed, filter].toArray() )
101155
assertEquals "Lee Butts", target.name
102156
assertNull target.email

test/web/org/codehaus/groovy/grails/web/binding/GrailsDataBinderTests.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ public void testBindStructuredDateWithMonthPrecision() throws Exception {
5757
testBindStructuredDate("1999", "1", null, null, null); // January 1st, 1999 - 00:00
5858
testBindStructuredDate("1999", "12", null, null, null); // December 1st, 1999 - 00:00
5959
}
60+
61+
public void testAllowedAndDissallowedDefaultToEmptyArray(){
62+
TestBean testBean = new TestBean();
63+
GrailsDataBinder binder = new GrailsDataBinder(testBean,"testBean");
64+
assertNotNull( binder.getAllowedFields());
65+
assertEquals(0, binder.getAllowedFields().length);
66+
assertNotNull( binder.getDisallowedFields());
67+
assertEquals(0, binder.getDisallowedFields().length);
68+
}
6069

6170
public void testBindStructuredDateWithDayPrecision() throws Exception {
6271
testBindStructuredDate("2012", "2", "1", null, null); // February 1, 2012 - 00:00
@@ -105,7 +114,7 @@ private void testBindInvalidStructuredDate(String year, String month, String da
105114

106115
assertNull(testBean.getMyDate());
107116
}
108-
117+
109118
public void testFiltersRequestParams(){
110119
MockHttpServletRequest request = new MockHttpServletRequest();
111120
request.addParameter("joe.name","joe");
@@ -171,7 +180,7 @@ public void testBindingWithPrefix() throws Exception {
171180
assertEquals(33, author2.getAge());
172181
}
173182

174-
183+
175184

176185

177186
/**

0 commit comments

Comments
 (0)