Skip to content

Commit 637a70e

Browse files
Merge branch '3.1.x' into 3.2.x
2 parents 5aa2263 + 0ae8a63 commit 637a70e

File tree

4 files changed

+73
-4
lines changed

4 files changed

+73
-4
lines changed

grails-core/src/main/groovy/grails/util/GrailsClassUtils.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -734,11 +734,50 @@ public static boolean isGetter(String name, Class<?>[] args) {
734734

735735
if (name.startsWith("get")) {
736736
name = name.substring(3);
737-
if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true;
737+
if (isPropertyMethodSuffix(name)) return true;
738738
}
739739
else if (name.startsWith("is")) {
740740
name = name.substring(2);
741-
if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true;
741+
if (isPropertyMethodSuffix(name)) return true;
742+
}
743+
return false;
744+
}
745+
746+
/**
747+
* This method is used when interrogating a method name to determine if the
748+
* method represents a property getter. For example, if a method is named
749+
* <code>getSomeProperty</code>, the value <code>"SomeProperty"</code> could
750+
* be passed to this method to determine that the method should be considered
751+
* a property getter. Examples of suffixes that would be considered property
752+
* getters:
753+
* <ul>
754+
* <li>SomeProperty</li>
755+
* <li>Word</li>
756+
* <li>aProperty</li>
757+
* <li>S</li>
758+
* </ul>
759+
*
760+
* Example sof suffixes that would not be considered property getters:
761+
* <ul>
762+
* <li>someProperty</li>
763+
* <li>word</li>
764+
* <li>s</li>
765+
* </ul>
766+
* @param suffix The suffix to inspect
767+
* @return true if suffix indicates a property name
768+
*/
769+
protected static boolean isPropertyMethodSuffix(String suffix) {
770+
if (suffix.length() > 0) {
771+
if(suffix.length() == 1) {
772+
if(Character.isUpperCase(suffix.charAt(0))) {
773+
return true;
774+
}
775+
} else {
776+
if(Character.isUpperCase(suffix.charAt(0)) ||
777+
(Character.isUpperCase(suffix.charAt(1)) && Character.isLowerCase(suffix.charAt(0)))) {
778+
return true;
779+
}
780+
}
742781
}
743782
return false;
744783
}

grails-test-suite-uber/src/test/groovy/grails/validation/DomainClassValidationSpec.groovy

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class DomainClassValidationSpec extends Specification {
3939

4040
void 'Test validate can be invoked in a unit test with no special configuration'() {
4141
when: 'an object is valid'
42-
def domainClass = new MyDomainClass(name: 'Kirk', age: 47, town: 'STL')
42+
def domainClass = new MyDomainClass(name: 'Kirk', age: 47, town: 'STL', aString: 'Woot!')
4343

4444
then: 'validate() returns true and there are no errors'
4545
domainClass.validate()
@@ -85,6 +85,7 @@ class DomainClassValidationSpec extends Specification {
8585
domainClass.errors.getFieldError('age').rejectedValue == 'type mismatched'
8686

8787
when:
88+
domainClass.aString = 'Woot!'
8889
domainClass.name = 'lower case'
8990
domainClass.age = -1 // invalid value
9091
domainClass.town = ''
@@ -111,6 +112,7 @@ class DomainClassValidationSpec extends Specification {
111112
domainClass.errors.getFieldError('age').rejectedValue == 'any validation failure'
112113

113114
when:
115+
domainClass.aString = 'Woot!'
114116
domainClass.name = 'lower case'
115117
domainClass.age = -1 // invalid value
116118
domainClass.town = ''
@@ -137,17 +139,19 @@ class DomainClassValidationSpec extends Specification {
137139
def constraints = getAssociatedDomainClassFromApplication(new MyDomainClass()).getConstrainedProperties()
138140

139141
then:
140-
constraints.size() == 4
142+
constraints.size() == 5
141143
constraints.containsKey 'name'
142144
constraints.containsKey 'town'
143145
constraints.containsKey 'age'
144146
constraints.containsKey 'someProperty'
147+
constraints.containsKey 'aString'
145148

146149
and:
147150
constraints.name.appliedConstraints.size() == 2
148151
constraints.age.appliedConstraints.size() == 2
149152
constraints.town.appliedConstraints.size() == 1
150153
constraints.someProperty.appliedConstraints.size() == 1
154+
constraints.aString.appliedConstraints.size() == 1
151155

152156
and:
153157
constraints.name.hasAppliedConstraint 'matches'
@@ -156,12 +160,14 @@ class DomainClassValidationSpec extends Specification {
156160
constraints.age.hasAppliedConstraint 'nullable'
157161
constraints.town.hasAppliedConstraint 'nullable'
158162
constraints.someProperty.hasAppliedConstraint 'nullable'
163+
constraints.aString.hasAppliedConstraint 'nullable'
159164

160165
and: 'implicit defaultNullable is nullable:false'
161166
!constraints.name.nullable
162167
!constraints.age.nullable
163168
!constraints.town.nullable
164169
!constraints.someProperty.nullable
170+
!constraints.aString.nullable
165171
}
166172

167173
@Ignore('defaultNullable is not supported yet')
@@ -307,6 +313,7 @@ class MyDomainClass {
307313
String name
308314
Integer age
309315
String town
316+
String aString
310317
private String _someProperty = 'default value'
311318

312319
void setSomeOtherProperty(String s) {}

grails-test-suite-uber/src/test/groovy/org/grails/commons/GrailsClassUtilsTests.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import grails.util.GrailsClassUtils;
1818
import junit.framework.TestCase;
19+
import spock.lang.Issue;
1920

2021
import javax.servlet.http.HttpServletRequest;
2122
import java.util.Date;
@@ -311,6 +312,14 @@ public void testIsPropertyGetter() throws Exception {
311312
assertFalse(GrailsClassUtils.isPropertyGetter(ClassHavingPropertyGetters.class.getDeclaredMethod("getFilename", String.class)));
312313
assertFalse(GrailsClassUtils.isPropertyGetter(ClassHavingPropertyGetters.class.getDeclaredMethod("getTitle", null)));
313314
}
315+
316+
@Issue("https://github.com/grails/grails-core/issues/10343")
317+
public void testPropertiesBeginningWithSingleLowerCaseLetter() throws Exception {
318+
assertTrue(GrailsClassUtils.isPropertyGetter(SomeGroovyClass.class.getDeclaredMethod("getaString", null)));
319+
assertTrue(GrailsClassUtils.isPropertyGetter(SomeGroovyClass.class.getDeclaredMethod("isaBoolean", null)));
320+
assertTrue(GrailsClassUtils.isPropertyGetter(SomeGroovyClass.class.getDeclaredMethod("getS", null)));
321+
assertTrue(GrailsClassUtils.isPropertyGetter(SomeGroovyClass.class.getDeclaredMethod("isB", null)));
322+
}
314323
}
315324

316325
class ClassWithStaticFieldAndStaticPropertyWithSameName {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.grails.commons
2+
3+
class SomeGroovyClass {
4+
String s
5+
String aString
6+
7+
boolean isaBoolean() {
8+
true
9+
}
10+
11+
boolean isB() {
12+
true
13+
}
14+
}

0 commit comments

Comments
 (0)