in lang/java/avro/src/main/java/org/apache/avro/Resolver.java [722:799]
private static boolean unionEquiv(Schema write, Schema read, Map<SeenPair, Boolean> seen) {
final Schema.Type wt = write.getType();
if (wt != read.getType()) {
return false;
}
// Previously, the spec was somewhat ambiguous as to whether getFullName or
// getName should be used here. Using name rather than fully qualified name
// maintains backwards compatibility.
if ((wt == Schema.Type.RECORD || wt == Schema.Type.FIXED || wt == Schema.Type.ENUM)
&& !(write.getName() == null || write.getName().equals(read.getName()))) {
return false;
}
switch (wt) {
case NULL:
case BOOLEAN:
case INT:
case LONG:
case FLOAT:
case DOUBLE:
case STRING:
case BYTES:
return true;
case ARRAY:
return unionEquiv(write.getElementType(), read.getElementType(), seen);
case MAP:
return unionEquiv(write.getValueType(), read.getValueType(), seen);
case FIXED:
return write.getFixedSize() == read.getFixedSize();
case ENUM: {
final List<String> ws = write.getEnumSymbols();
final List<String> rs = read.getEnumSymbols();
return ws.equals(rs);
}
case UNION: {
final List<Schema> wb = write.getTypes();
final List<Schema> rb = read.getTypes();
if (wb.size() != rb.size()) {
return false;
}
for (int i = 0; i < wb.size(); i++) {
if (!unionEquiv(wb.get(i), rb.get(i), seen)) {
return false;
}
}
return true;
}
case RECORD: {
final SeenPair wsc = new SeenPair(write, read);
if (!seen.containsKey(wsc)) {
seen.put(wsc, true); // Be optimistic, but we may change our minds
final List<Field> wb = write.getFields();
final List<Field> rb = read.getFields();
if (wb.size() != rb.size()) {
seen.put(wsc, false);
} else {
for (int i = 0; i < wb.size(); i++) {
// Loop through each of the elements, and check if they are equal
if (!wb.get(i).name().equals(rb.get(i).name())
|| !unionEquiv(wb.get(i).schema(), rb.get(i).schema(), seen)) {
seen.put(wsc, false);
break;
}
}
}
}
return seen.get(wsc);
}
default:
throw new IllegalArgumentException("Unknown schema type: " + write.getType());
}
}