Java API toString, compareTo, equals, hashCode
A discussion of the Java API implementation of toString(), compareTo(), equals() and hashCode(), and a comparison with how the Python API does it.
All Java objects automatically have toString(), equals() and hashCode() function, which can be overridden. You can also add a compareTo() function, so as to implement the Comparable() interface.
The MemopsObject and MemopsDataTypeObject() versions are different. For example, the former compare by key/id and the latter by value. So we discuss them separately below.
- int compareTo(Object other)
- int hashCode()
- boolean equals(Object other)
We want:
(e1.compareTo((Object)e2) == 0) should have the same boolean value as e1.equals((Object)e2), except that e1.comparesTo(null) throws a NullPointerException and e1.equals(null) returns false (if e1 is not itself null).
e1.equals(e2) implies that e1.hashCode() == e2.hashCode()
Below we write Id() instead of System.identityHashCode() to make the statements more succinct.
MemopsObject
The functions equals() and hashCode() are not overridden.
We only implement compareTo() for certain classes:
MemopsRoot:
result = Id(this) - Id(other);
Other classes (MetaClass clazz, API class APIclass)
The following determines whether we have a compareTo function:
haveCompareTo = false
parentRole = clazz.parentRole
if parentRole:
ot = parentRole.otherRole
if inClass.keyNames or (ot is not None and ot.hicard == 1):
if parentRole.container is clazz:
haveCompareTo = True
If we have a compareTo function then the code is:
if (other instanceof APIclass)
{
memops.api.Implementation.MemopsRoot p2 = other.getRoot();
result = p1.compareTo(p2);
if (result == 0) {
List<Object> l1 = getFullKey();
List<Object> l2 = other.getFullKey();
Iterator<Object> iter1 = l1.iterator();
Iterator<Object> iter2 = l2.iterator();
while (iter1.hasNext()) {
result = ((Comparable) iter1.next()).compareTo((Comparable) iter2.next());
if (result != 0)
break;
}
}
}
else
{
result = Id(this) - Id(other);
}The Python API does not implement __equals__, __hash__ or __cmp__, so effectively uses Id/== in all cases. This is functionally equivalent to the Java methods.
The toString() function is implemented as (leaving out some exception handling which obscures the code):
String className = getQualifiedName();
List<Object> key;
key = getFullKey();
StringBuffer ss = new StringBuffer("<");
ss.append(className);
if (getIsDeleted())
ss.append(" (deleted)");
ss.append(" [");
if (key.isEmpty()) {
key.add(hashCode());
ss.append("id:");
}
int nn = 0;
for (Object oo: key) {
if (nn > 0)
ss.append(", ");
ss.append(oo);
nn++;
}
ss.append("]>");
result = ss.toString();
The Python API __repr__ should produce the same (at least to within differences in whitespace).
MemopsDataTypeObject
We override equals(), hashCode() and compareTo() for all non-abstract classes.
The code for equals() is:
if (other == this) {
result = true;
} else if (other == null) {
result = false;
} else {
int rr = compareTo(other);
result = (rr == 0 ? true : false);
}
The code for hashCode() is generated from the following (morally what is going on is that the attribute values determine the hashCode):
elems = complexDataType.getAllAttributes()
elems = [ elem for elem in elems if not elem.isDerived and not elem.isImplementation ]
elems = metaUtil.sortByAttribute(elems, 'name')
self.writeOne('result = 0;')
for elem in elems:
ss = uniUtil.upperFirst(elem.name)
self.write("""
try {
java.lang.Object oo = get%s();
// below same idea as for java.util.List.hashCode()
""" % ss)
if elem.locard == 0:
self.write("""
result = 31*result + (oo == null ? 0 : oo.hashCode());
""")
else:
self.write("""
result = 31*result + oo.hashCode();
""")
self.write("""
} catch (memops.general.ApiException aexc) {
}
""")
In the Python API there is no __equals__ implemented.
The code for compareTo() is generated from the following (morally what is going on is that attributes are compared):
elems = complexDataType.getAllAttributes()
elems = [ elem for elem in elems if not elem.isDerived and not elem.isImplementation ]
elems = metaUtil.sortByAttribute(elems, 'name')
tt = self.elementVarType(complexDataType)
self.write("""
if (getClass().equals(other.getClass())) {
result = 0;
%s oo = (%s) other;
while (true) { // only here because of break(s) below
""" % (tt, tt))
nn = 0
for elem in elems:
if nn > 0:
self.write("""
if (result != 0)
break;
""")
ss = uniUtil.upperFirst(elem.name)
tt = self.elementVarType(elem)
if elem.hicard == 1:
if elem.locard == 0:
self.write("""
try {
%s vv = get%s();
%s ww = oo.get%s();
if ((vv == null) && (ww == null)) {
// result = 0 still
} else if ((vv == null) && (ww != null)) {
result = -1;
} else if ((vv != null) && (ww == null)) {
result = 1;
} else {
result = vv.compareTo(ww);
}
} catch (memops.general.ApiException aexc) {
}
""" % (tt, ss, tt, ss))
else: # hicard = locard = 1
self.write("""
try {
result = get%s().compareTo(oo.get%s());
} catch (memops.general.ApiException aexc) {
}
""" % (ss, ss))
else: # hicard != 1
if elem.isOrdered:
ff = 'get'
else:
ff = 'sorted'
self.write("""
try {
java.util.List<%s> o1 = %s%s();
java.util.List<%s> o2 = oo.%s%s();
result = o1.size() - o2.size();
if (result != 0)
break;
java.util.Iterator<%s> iter1 = o1.iterator();
java.util.Iterator<%s> iter2 = o2.iterator();
while (iter1.hasNext()) {
result = iter1.next().compareTo(iter2.next());
if (result != 0)
break;
}
} catch (memops.general.ApiException aexc) {
}
""" % (tt, ff, ss, tt, ff, ss, tt, tt))
nn = nn + 1
self.write("""
break;
}
} else {
result = java.lang.System.identityHashCode(this) - java.lang.System.identityHashCode(other);
}
""")
In the Python API __cmp__ also sorts on the attribute names but for hicard != 1 just uses the getter in all cases, whereas the Java uses the getter only if isOrdered=True, otherwise it uses the sorted function.
The code for toString() is generated from the following (morally what is going on is that the qualifiedName is shown and then the attributes):
elems = complexDataType.getAllAttributes()
elems = [ elem for elem in elems if not elem.isDerived and not elem.isImplementation ]
elems = metaUtil.sortByAttribute(elems, 'name')
self.writeOne('java.lang.StringBuffer ss = new java.lang.StringBuffer("<%s [");' % complexDataType.qualifiedName())
nn = 0
for elem in elems:
if nn > 0:
self.writeOne('ss.append(", ");')
self.write('''
ss.append("%s");
ss.append("=");
ss.append("\\"");
try {
ss.append(get%s().toString());
ss.append("\\"");
} catch (memops.general.ApiException aexc) {
int nn = ss.length();
ss.deleteCharAt(nn-1);
ss.append("??????");
} catch (java.lang.NullPointerException aexc) {
int nn = ss.length();
ss.deleteCharAt(nn-1);
ss.append("null");
}
''' % (elem.name, uniUtil.upperFirst(elem.name)))
nn = nn + 1
self.writeOne('ss.append("]>");')
self.writeOne('%s = ss.toString();' % self.varNames['result'])
The Python API __repr__ function is approximately the same as the above but the punctuation is different.