OPENJPA-219. Avoid Class.getDeclaredField() / Class.getDeclaredMethod() in Reflection, since they throw exceptions as a side-effect. Also contains assorted clean-up in ClassMetaData.

git-svn-id: https://svn.apache.org/repos/asf/incubator/openjpa/trunk@532137 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Patrick Linskey 2007-04-24 23:34:46 +00:00
parent 6bad8ff715
commit 0e7feca04a
3 changed files with 82 additions and 49 deletions

View File

@ -687,15 +687,8 @@ public class PCEnhancer {
Class owner) { Class owner) {
// find the actual ancestor class that declares the field, then // find the actual ancestor class that declares the field, then
// check if the class is persistent, and if the field is managed // check if the class is persistent, and if the field is managed
for (; !owner.getName().equals(Object.class.getName()); Field f = Reflection.findField(owner, fieldName, false);
owner = owner.getSuperclass()) { if (f == null)
try {
owner.getDeclaredField(fieldName);
break;
} catch (Exception e) {
}
}
if (owner.getName().equals(Object.class.getName()))
return null; return null;
// managed interface // managed interface

View File

@ -47,20 +47,20 @@ public class Reflection {
public static Method findGetter(Class cls, String prop, boolean mustExist) { public static Method findGetter(Class cls, String prop, boolean mustExist) {
prop = StringUtils.capitalize(prop); prop = StringUtils.capitalize(prop);
String name = "get" + prop; String name = "get" + prop;
Method m;
try { try {
for (Class c = cls; c != null && c != Object.class; // this algorithm searches for a get<prop> or is<prop> method in
// a breadth-first manner.
for (Class c = cls; c != null && c != Object.class;
c = c.getSuperclass()) { c = c.getSuperclass()) {
try { m = getDeclaredMethod(c, name, null);
return c.getDeclaredMethod(name, (Class[]) null); if (m != null) {
} catch (NoSuchMethodException nsme) { return m;
try { } else {
Method m = c.getDeclaredMethod("is" + prop, m = getDeclaredMethod(c, "is" + prop, null);
(Class[]) null); if (m != null && (m.getReturnType() == boolean.class
if (m != null && (m.getReturnType() == boolean.class || m.getReturnType() == Boolean.class))
|| m.getReturnType() == Boolean.class)) return m;
return m;
} catch (NoSuchMethodException nsme2) {
}
} }
} }
} catch (Exception e) { } catch (Exception e) {
@ -89,14 +89,13 @@ public class Reflection {
public static Method findSetter(Class cls, String prop, Class param, public static Method findSetter(Class cls, String prop, Class param,
boolean mustExist) { boolean mustExist) {
String name = "set" + StringUtils.capitalize(prop); String name = "set" + StringUtils.capitalize(prop);
Class[] params = new Class[] { param }; Method m;
try { try {
for (Class c = cls; c != null && c != Object.class; for (Class c = cls; c != null && c != Object.class;
c = c.getSuperclass()) { c = c.getSuperclass()) {
try { m = getDeclaredMethod(c, name, param);
return c.getDeclaredMethod(name, params); if (m != null)
} catch (NoSuchMethodException nsme) { return m;
}
} }
} catch (Exception e) { } catch (Exception e) {
throw new GeneralException(e); throw new GeneralException(e);
@ -108,17 +107,41 @@ public class Reflection {
} }
/** /**
* Return the field with the given name, optionally throwing an exception * Invokes <code>cls.getDeclaredMethods()</code>, and returns the method
* that matches the <code>name</code> and <code>param</code> arguments.
* Avoids the exception thrown by <code>Class.getDeclaredMethod()</code>
* for performance reasons. <code>param</code> may be null.
*
* @since 0.9.8
*/
private static Method getDeclaredMethod(Class cls, String name,
Class param) {
Method[] methods = cls.getDeclaredMethods();
for (int i = 0 ; i < methods.length; i++) {
if (name.equals(methods[i].getName())) {
Class[] methodParams = methods[i].getParameterTypes();
if (param == null && methodParams.length == 0)
return methods[i];
if (param != null && methodParams.length == 1
&& param.equals(methodParams[0]))
return methods[i];
}
}
return null;
}
/**
* Return the field with the given name, optionally throwing an exception
* if none. * if none.
*/ */
public static Field findField(Class cls, String name, boolean mustExist) { public static Field findField(Class cls, String name, boolean mustExist) {
try { try {
for (Class c = cls; c != null && c != Object.class; Field f;
for (Class c = cls; c != null && c != Object.class;
c = c.getSuperclass()) { c = c.getSuperclass()) {
try { f = getDeclaredField(c, name);
return c.getDeclaredField(name); if (f != null)
} catch (NoSuchFieldException nsfe) { return f;
}
} }
} catch (Exception e) { } catch (Exception e) {
throw new GeneralException(e); throw new GeneralException(e);
@ -129,6 +152,22 @@ public class Reflection {
return null; return null;
} }
/**
* Invokes <code>cls.getDeclaredFields()</code>, and returns the field
* that matches the <code>name</code> argument. Avoids the exception
* thrown by <code>Class.getDeclaredField()</code> for performance reasons.
*
* @since 0.9.8
*/
private static Field getDeclaredField(Class cls, String name) {
Field[] fields = cls.getDeclaredFields();
for (int i = 0 ; i < fields.length; i++) {
if (name.equals(fields[i].getName()))
return fields[i];
}
return null;
}
/** /**
* Return the value of the given field in the given object. * Return the value of the given field in the given object.
*/ */

View File

@ -55,7 +55,6 @@ import org.apache.openjpa.util.InternalException;
import org.apache.openjpa.util.LongId; import org.apache.openjpa.util.LongId;
import org.apache.openjpa.util.MetaDataException; import org.apache.openjpa.util.MetaDataException;
import org.apache.openjpa.util.ObjectId; import org.apache.openjpa.util.ObjectId;
import org.apache.openjpa.util.OpenJPAException;
import org.apache.openjpa.util.OpenJPAId; import org.apache.openjpa.util.OpenJPAId;
import org.apache.openjpa.util.ShortId; import org.apache.openjpa.util.ShortId;
import org.apache.openjpa.util.StringId; import org.apache.openjpa.util.StringId;
@ -117,6 +116,10 @@ public class ClassMetaData
private static final Localizer _loc = Localizer.forPackage private static final Localizer _loc = Localizer.forPackage
(ClassMetaData.class); (ClassMetaData.class);
private static final FetchGroup[] EMPTY_FETCH_GROUP_ARRAY
= new FetchGroup[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private MetaDataRepository _repos; private MetaDataRepository _repos;
private transient ClassLoader _loader = null; private transient ClassLoader _loader = null;
@ -784,8 +787,9 @@ public class ClassMetaData
synchronized (_ifaceMap) { synchronized (_ifaceMap) {
Map fields = (Map) _ifaceMap.get(iface); Map fields = (Map) _ifaceMap.get(iface);
if (fields == null) if (fields == null)
return new String[0]; return EMPTY_STRING_ARRAY;
return (String[]) fields.keySet().toArray(new String[0]); return (String[]) fields.keySet().toArray(
new String[fields.size()]);
} }
} }
@ -1397,15 +1401,12 @@ public class ClassMetaData
if (fieldName == null || SYNTHETIC.equals(fieldName)) if (fieldName == null || SYNTHETIC.equals(fieldName))
return null; return null;
for (Class type = _type; type != null && type != Object.class; Field f = Reflection.findField(_type, fieldName, false);
type = type.getSuperclass()) { if (f != null)
try { return f;
return type.getDeclaredField(fieldName); else
} catch (Exception e) { throw new MetaDataException(
} _loc.get("no-detach-state", fieldName, _type));
}
throw new MetaDataException(_loc.get("no-detach-state", fieldName,
_type));
} }
/** /**
@ -1823,7 +1824,7 @@ public class ClassMetaData
ClassMetaData embed = pks[0].getEmbeddedMetaData(); ClassMetaData embed = pks[0].getEmbeddedMetaData();
validateAppIdClassMethods(embed.getDescribedType()); validateAppIdClassMethods(embed.getDescribedType());
validateAppIdClassPKs(embed, embed.getFields(), validateAppIdClassPKs(embed, embed.getFields(),
embed.getDescribedType(), runtime); embed.getDescribedType());
} }
return; return;
} }
@ -1850,7 +1851,7 @@ public class ClassMetaData
validateAppIdClassMethods(_objectId); validateAppIdClassMethods(_objectId);
// make sure the app id class has all pk fields // make sure the app id class has all pk fields
validateAppIdClassPKs(this, pks, _objectId, runtime); validateAppIdClassPKs(this, pks, _objectId);
} }
} }
@ -1904,7 +1905,7 @@ public class ClassMetaData
* Validate that the primary key class has all pk fields. * Validate that the primary key class has all pk fields.
*/ */
private void validateAppIdClassPKs(ClassMetaData meta, private void validateAppIdClassPKs(ClassMetaData meta,
FieldMetaData[] fmds, Class oid, boolean runtime) { FieldMetaData[] fmds, Class oid) {
if (fmds.length == 0 && !Modifier.isAbstract(meta.getDescribedType(). if (fmds.length == 0 && !Modifier.isAbstract(meta.getDescribedType().
getModifiers())) getModifiers()))
throw new MetaDataException(_loc.get("no-pk", _type)); throw new MetaDataException(_loc.get("no-pk", _type));
@ -2012,7 +2013,7 @@ public class ClassMetaData
*/ */
public FetchGroup[] getDeclaredFetchGroups() { public FetchGroup[] getDeclaredFetchGroups() {
if (_fgs == null) if (_fgs == null)
_fgs = (_fgMap == null) ? new FetchGroup[0] : (FetchGroup[]) _fgs = (_fgMap == null) ? EMPTY_FETCH_GROUP_ARRAY : (FetchGroup[])
_fgMap.values().toArray(new FetchGroup[_fgMap.size()]); _fgMap.values().toArray(new FetchGroup[_fgMap.size()]);
return _fgs; return _fgs;
} }