Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 67 additions & 35 deletions grails-core/src/main/groovy/grails/util/GrailsClassUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -722,11 +722,12 @@ public static String getSetterName(String propertyName) {
}

/**
* Returns true if the name of the method specified and the number of arguments make it a javabean property
* Returns true if the name of the method specified and the number of arguments make it a javabean property getter.
* The name is assumed to be a valid Java method name, that is not verified.
*
* @param name True if its a Javabean property
* @param name The name of the method
* @param args The arguments
* @return true if it is a javabean property method
* @return true if it is a javabean property getter
*/
public static boolean isGetter(String name, Class<?>[] args) {
if (!StringUtils.hasText(name) || args == null)return false;
Expand Down Expand Up @@ -755,90 +756,121 @@ else if (name.startsWith("is")) {
* <li>Word</li>
* <li>aProperty</li>
* <li>S</li>
* <li>X567</li>
* </ul>
*
* Example sof suffixes that would not be considered property getters:
* Examples of suffixes that would not be considered property getters:
* <ul>
* <li>someProperty</li>
* <li>word</li>
* <li>s</li>
* <li>x567</li>
* <li>2other</li>
* <li>5</li>
* </ul>
*
* A suffix like <code>prop</code> from a method <code>getprop()</code> is
* not recognized as a valid suffix. However Groovy will recognize such a
* method as a property getter but only if a method <code>getProp()</code> or
* a property <code>prop</code> does not also exist. The Java Beans
* specification is unclear on how to treat such method names, it only says
* that "by default" the suffix will start with a capital letter because of
* the camel case style usually used. (See the JavaBeans API specification
* sections 8.3 and 8.8.)
*
* This method assumes that all characters in the name are valid Java identifier
* letters.
*
* @param suffix The suffix to inspect
* @return true if suffix indicates a property name
*/
protected static boolean isPropertyMethodSuffix(String suffix) {
if (suffix.length() > 0) {
if(suffix.length() == 1) {
if(Character.isUpperCase(suffix.charAt(0))) {
return true;
}
} else {
if(Character.isUpperCase(suffix.charAt(0)) ||
(Character.isUpperCase(suffix.charAt(1)) && Character.isLowerCase(suffix.charAt(0)))) {
return true;
}
}
}
return false;
if(suffix.length() == 0) return false;
if(!Character.isJavaIdentifierStart(suffix.charAt(0))) return false;
if(suffix.length() == 1) return Character.isUpperCase(suffix.charAt(0));
return Character.isUpperCase(suffix.charAt(0)) || Character.isUpperCase(suffix.charAt(1));
}

/**
* Returns a property name equivalent for the given getter name or null if it is not a getter
* Returns a property name equivalent for the given getter name or null if it is not a valid getter. If not null
* or empty the getter name is assumed to be a valid identifier.
*
* @param getterName The getter name
* @return The property name equivalent
*/
public static String getPropertyForGetter(String getterName) {
if (!StringUtils.hasText(getterName))return null;
if (getterName == null || getterName.length() == 0) return null;

if (getterName.startsWith("get")) {
String prop = getterName.substring(3);
return convertPropertyName(prop);
return convertValidPropertyMethodSuffix(prop);
}
if (getterName.startsWith("is")) {
String prop = getterName.substring(2);
return convertPropertyName(prop);
return convertValidPropertyMethodSuffix(prop);
}
return null;
}

private static String convertPropertyName(String prop) {
if (prop.length() == 1) {
return prop.toLowerCase();
/**
* This method functions the same as {@link #isPropertyMethodSuffix(String)},
* but in addition returns the property name, or null if not a valid property.
*
* @param suffix The suffix to inspect
* @return The property name or null
*/
private static String convertValidPropertyMethodSuffix(String suffix) {
if (suffix.length() == 0) return null;

// We assume all characters are Character.isJavaIdentifierPart, but the first one may not be a valid
// starting character.
if (!Character.isJavaIdentifierStart(suffix.charAt(0))) return null;

if (suffix.length() == 1) {
return Character.isUpperCase(suffix.charAt(0)) ? suffix.toLowerCase() : null;
}
if (Character.isUpperCase(prop.charAt(0)) && Character.isUpperCase(prop.charAt(1))) {
return prop;
if (Character.isUpperCase(suffix.charAt(1))) {
// "aProperty", "AProperty"
return suffix;
}
if (Character.isDigit(prop.charAt(0))) {
return prop;
if (Character.isUpperCase(suffix.charAt(0))) {
return Character.toLowerCase(suffix.charAt(0)) + suffix.substring(1);
}
return Character.toLowerCase(prop.charAt(0)) + prop.substring(1);
return null;
}

/**
* Returns a property name equivalent for the given setter name or null if it is not a getter
* Returns a property name equivalent for the given setter name or null if it is not a valid setter. If not null
* or empty the setter name is assumed to be a valid identifier.
*
* @param setterName The setter name
* @param setterName The setter name, must be null or empty or a valid identifier name
* @return The property name equivalent
*/
public static String getPropertyForSetter(String setterName) {
if (!StringUtils.hasText(setterName))return null;
if (setterName == null || setterName.length() == 0) return null;

if (setterName.startsWith("set")) {
String prop = setterName.substring(3);
return convertPropertyName(prop);
return convertValidPropertyMethodSuffix(prop);
}
return null;
}

/**
* Returns true if the name of the method specified and the number of arguments make it a javabean property setter.
* The name is assumed to be a valid Java method name, that is not verified.
*
* @param name The name of the method
* @param args The arguments
* @return true if it is a javabean property setter
*/
@SuppressWarnings("rawtypes")
public static boolean isSetter(String name, Class[] args) {
if (!StringUtils.hasText(name) || args == null)return false;

if (name.startsWith("set")) {
if (args.length != 1) return false;
name = name.substring(3);
if (name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true;
return isPropertyMethodSuffix(name.substring(3));
}

return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,31 +126,24 @@ public void doWith(CachedMethod method) throws IllegalArgumentException,
if (!method.isPublic()) {
return;
}
if (returnType != Void.class && returnType != void.class) {
if (method.getParameterTypes().length == 0) {
String name = method.getName();
if (name.indexOf('$') == -1) {
if (name.length() > 3 && name.startsWith("get")
&& Character.isUpperCase(name.charAt(3))) {
name = name.substring(3);
} else if (name.length() > 2
&& name.startsWith("is")
&& Character.isUpperCase(name.charAt(2))
&& (returnType == Boolean.class ||
returnType == boolean.class)) {
name = name.substring(2);
}
if (method.isStatic()) {
GetterPropertyFetcher fetcher = new GetterPropertyFetcher(method, true);
staticFetchers.put(name, fetcher);
staticFetchers.put(StringUtils.uncapitalize(name),
fetcher);
} else {
instanceFetchers.put(StringUtils.uncapitalize(name),
new GetterPropertyFetcher(method, false));
}
}
}
if (returnType == Void.class || returnType == void.class || method.getParameterTypes().length != 0) {
return;
}

String propertyName = GrailsClassUtils.getPropertyForGetter(method.getName());
if(propertyName == null || propertyName.indexOf('$') != -1) {
return;
}

if (method.getName().startsWith("is") &&
!(returnType == Boolean.class || returnType == boolean.class)) {
return;
}

if (method.isStatic()) {
staticFetchers.put(propertyName, new GetterPropertyFetcher(method, true));
} else {
instanceFetchers.put(propertyName, new GetterPropertyFetcher(method, false));
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,20 @@ class ClassPropertyFetcherSpec extends Specification {
cpf.getPropertyValue(new Author(name: "Fred"),"name") == "Fred"
cpf.getPropertyValue(new Author(name: "Fred", books: ["test"]),"books").contains "test"
}

void "test properties that have the fifth letter of their getter capitalized instead of the fourth"() {
when:"A class property fetcher is created"
def cpf = ClassPropertyFetcher.forClass(Person)

then:"all properties are correct"
def person = new Person(name: "Fred", xAge: 30)
person.getxAge() == 30
cpf.getPropertyValue(person, "xAge") == 30
}
}
class Person {
String name
Integer xAge
}
class Author extends Person {
Set books
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,19 @@ public void testGetterNames() {
public void testIsGetterOrSetter() {
assertTrue(GrailsClassUtils.isSetter("setSomething", new Class[] { String.class }));
assertTrue(GrailsClassUtils.isGetter("getSomething", new Class[0]));
assertTrue(GrailsClassUtils.isGetter("isSomething", new Class[0]));
assertTrue(GrailsClassUtils.isSetter("setURL", new Class[] { String.class }));
assertTrue(GrailsClassUtils.isGetter("getURL", new Class[0]));
assertTrue(GrailsClassUtils.isGetter("isURL", new Class[0]));
assertTrue(GrailsClassUtils.isSetter("setaProp", new Class[] { String.class }));
assertTrue(GrailsClassUtils.isGetter("getaProp", new Class[0]));
assertTrue(GrailsClassUtils.isGetter("isaProp", new Class[0]));
assertTrue(GrailsClassUtils.isSetter("setX", new Class[] { String.class }));
assertTrue(GrailsClassUtils.isGetter("getX", new Class[0]));
assertTrue(GrailsClassUtils.isGetter("isX", new Class[0]));
assertTrue(GrailsClassUtils.isSetter("setX2", new Class[] { String.class }));
assertTrue(GrailsClassUtils.isGetter("getX2", new Class[0]));
assertTrue(GrailsClassUtils.isGetter("isX2", new Class[0]));

assertFalse(GrailsClassUtils.isGetter("something", new Class[] { String.class }));
assertFalse(GrailsClassUtils.isGetter("get", new Class[0]));
Expand All @@ -123,6 +134,20 @@ public void testIsGetterOrSetter() {
assertFalse(GrailsClassUtils.isSetter("setSomething", new Class[] { String.class, Object.class }));
assertFalse(GrailsClassUtils.isGetter("getSomething", new Class[] { Object.class }));

assertFalse(GrailsClassUtils.isGetter("getsomething", new Class[0]));
assertFalse(GrailsClassUtils.isGetter("issomething", new Class[0]));
assertFalse(GrailsClassUtils.isSetter("setsomething", new Class[] { String.class }));
assertFalse(GrailsClassUtils.isGetter("get0", new Class[0]));
assertFalse(GrailsClassUtils.isSetter("set0", new Class[] { String.class }));
assertFalse(GrailsClassUtils.isGetter("get2other", new Class[0]));
assertFalse(GrailsClassUtils.isSetter("set2other", new Class[] { String.class }));
assertFalse(GrailsClassUtils.isGetter("getq3", new Class[0]));
assertFalse(GrailsClassUtils.isSetter("setq3", new Class[] { String.class }));
assertFalse(GrailsClassUtils.isGetter("get5A", new Class[0]));
assertFalse(GrailsClassUtils.isSetter("set5A", new Class[] { String.class }));
assertFalse(GrailsClassUtils.isGetter("", new Class[0]));
assertFalse(GrailsClassUtils.isSetter("", new Class[] { String.class }));

assertFalse(GrailsClassUtils.isGetter(null, new Class[] { Object.class }));
assertFalse(GrailsClassUtils.isGetter("getSomething", null));
assertFalse(GrailsClassUtils.isGetter(null, null));
Expand All @@ -132,6 +157,21 @@ public void testGetPropertyForGetter() {
assertEquals("something", GrailsClassUtils.getPropertyForGetter("getSomething"));
assertEquals("URL", GrailsClassUtils.getPropertyForGetter("getURL"));
assertEquals("p", GrailsClassUtils.getPropertyForGetter("getP"));
assertEquals("URL", GrailsClassUtils.getPropertyForGetter("isURL"));
assertEquals("aProp", GrailsClassUtils.getPropertyForGetter("getaProp"));
assertEquals("x2", GrailsClassUtils.getPropertyForGetter("getX2"));
assertEquals("x2", GrailsClassUtils.getPropertyForGetter("isX2"));

assertNull(GrailsClassUtils.getPropertyForGetter(null));
assertNull(GrailsClassUtils.getPropertyForGetter(""));
assertNull(GrailsClassUtils.getPropertyForGetter("get0"));
assertNull(GrailsClassUtils.getPropertyForGetter("get2other"));
assertNull(GrailsClassUtils.getPropertyForGetter("getq3"));
assertNull(GrailsClassUtils.getPropertyForGetter("get5A"));
assertNull(GrailsClassUtils.getPropertyForGetter("setSomething"));
assertNull(GrailsClassUtils.getPropertyForGetter("getit"));
assertNull(GrailsClassUtils.getPropertyForGetter("geta"));
assertNull(GrailsClassUtils.getPropertyForGetter("get0"));
}

public void testGetStaticField() {
Expand Down