Reputation: 168
I've inherited some legacy code that fails if there's a circular reference. The code takes an object and builds the entire graph of objects for conversion to XML. The legacy code can't be modified, so I want to detect the reference and handle it accordingly.
For now, I'm building up a Set of all the fields on each object in the graph, and then later running through each objects fields and trying to detect if the objects are equal.
This is the portion of legacy code where the Set is built.
// declaration of set for this instance
Set<Object> noduplicates = new HashSet<Object>();
private Iterator<Field> findAllFields(Object o) {
Collection<Field> result = new LinkedList<Field>();j
Class<? extends Object> c = o.getClass();
while (c != null) {
Field[] f = c.getDeclaredFields();
for (int i = 0; i < f.length; i++) {
if (!Modifier.isStatic(f[i].getModifiers())) {
result.add(f[i]);
}
}
c = c.getSuperclass();
}
// add the fields for each object, for later comparison
noduplicates.addAll((Collection<?>) result);
testForDuplicates(noduplicates)
}
This is the current attempt at detecting circularity (inspired by : https://stackoverflow.com/a/2638662/817216):
private void testForDuplicates(Set<Object> noduplicates) throws ... {
for (Object object : noduplicates) {
Field[] fields = object.getClass().getFields();
for (Field field : fields) {
for (PropertyDescriptor pd : Introspector.getBeanInfo(field.getClass()).getPropertyDescriptors()) {
if (pd.getReadMethod() != null && !"class".equals(pd.getName())) {
Object possibleDuplicate = pd.getReadMethod().possibleDuplicate(object);
if (object.hashCode() == possibleDuplicate.hashCode()) {
System.out.println("Duplicated detected");
throw new RuntimeException();
}
}
}
}
}
}
I have a very simple test case, two POJOs each with a reference to the other:
class Prop {
private Loc loc;
public Loc getLoc() {
return loc;
}
public void setLoc(Loc loc) {
this.loc = loc;
}
}
class Loc {
private Prop prop;
public Prop getProp() {
return prop;
}
public void setProp(Prop prop) {
this.prop = prop;
}
}
I've tried several variations on the above, including direct testing of object equality. Currently testing on hashcode equality never detects the circularity.
Any pointers gratefully received.
Upvotes: 1
Views: 1341
Reputation: 168
Figured this out myself in the end. Part of the confusion I encountered using the Reflection API is that, for example, when you invoke Field.get(object);
object has to be an instance of the class that contains the field, which makes sense but is not immediately intuitive (it wasn't to me at least).
The docs state:
get(Object obj)
Returns the value of the field represented by this Field, on the specified object.
In the end the solution I came up with relies on storing a Map of each object's class type, and using that to determine if any of its fields reference it.
This is what I ended up with:
boolean testForDuplicates(Set<Object> noduplicates2) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException, IntrospectionException {
Map<Object, Object> duplicatesMap = new HashMap<Object, Object>();
for (Object object : noduplicates2) {
duplicatesMap.put(object.getClass(), object);
}
for (Object object : noduplicates2) {
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
if (duplicatesMap.containsKey(field.getType())) {
Object possibleDupeFromField = duplicatesMap.get(field.getType());
if (noduplicates2.contains(possibleDupeFromField)) {
return true;
}
}
}
}
return false;
}
I'll test it out in the offending real code, and update if I discover anything else.
Upvotes: 1