in lang/java/avro/src/main/java/org/apache/avro/reflect/ReflectData.java [617:776]
protected Schema createSchema(Type type, Map<String, Schema> names) {
if (type instanceof GenericArrayType) { // generic array
Type component = ((GenericArrayType) type).getGenericComponentType();
if (component == Byte.TYPE) // byte array
return Schema.create(Schema.Type.BYTES);
Schema result = Schema.createArray(createSchema(component, names));
setElement(result, component);
return result;
} else if (type instanceof ParameterizedType) {
ParameterizedType ptype = (ParameterizedType) type;
Class raw = (Class) ptype.getRawType();
Type[] params = ptype.getActualTypeArguments();
if (Map.class.isAssignableFrom(raw)) { // Map
Class key = (Class) params[0];
if (isStringable(key)) { // Stringable key
Schema schema = Schema.createMap(createSchema(params[1], names));
schema.addProp(KEY_CLASS_PROP, key.getName());
return schema;
} else if (key != String.class) {
Schema schema = createNonStringMapSchema(params[0], params[1], names);
schema.addProp(CLASS_PROP, raw.getName());
return schema;
}
} else if (Collection.class.isAssignableFrom(raw)) { // Collection
if (params.length != 1)
throw new AvroTypeException("No array type specified.");
Schema schema = Schema.createArray(createSchema(params[0], names));
schema.addProp(CLASS_PROP, raw.getName());
return schema;
}
} else if ((type == Byte.class) || (type == Byte.TYPE)) {
Schema result = Schema.create(Schema.Type.INT);
result.addProp(CLASS_PROP, Byte.class.getName());
return result;
} else if ((type == Short.class) || (type == Short.TYPE)) {
Schema result = Schema.create(Schema.Type.INT);
result.addProp(CLASS_PROP, Short.class.getName());
return result;
} else if ((type == Character.class) || (type == Character.TYPE)) {
Schema result = Schema.create(Schema.Type.INT);
result.addProp(CLASS_PROP, Character.class.getName());
return result;
} else if (type instanceof Class) { // Class
Class<?> c = (Class<?>) type;
while (c.isAnonymousClass()) {
c = c.getSuperclass();
}
if (c.isPrimitive() || // primitives
c == Void.class || c == Boolean.class || c == Integer.class || c == Long.class || c == Float.class
|| c == Double.class || c == Byte.class || c == Short.class || c == Character.class)
return super.createSchema(type, names);
if (c.isArray()) { // array
Class component = c.getComponentType();
if (component == Byte.TYPE) { // byte array
Schema result = Schema.create(Schema.Type.BYTES);
result.addProp(CLASS_PROP, c.getName());
return result;
}
Schema result = Schema.createArray(createSchema(component, names));
result.addProp(CLASS_PROP, c.getName());
setElement(result, component);
return result;
}
AvroSchema explicit = c.getAnnotation(AvroSchema.class);
if (explicit != null) // explicit schema
return new Schema.Parser().parse(explicit.value());
if (CharSequence.class.isAssignableFrom(c)) // String
return Schema.create(Schema.Type.STRING);
if (ByteBuffer.class.isAssignableFrom(c)) // bytes
return Schema.create(Schema.Type.BYTES);
if (Collection.class.isAssignableFrom(c)) // array
throw new AvroRuntimeException("Can't find element type of Collection");
Conversion<?> conversion = getConversionByClass(c);
if (conversion != null) {
return conversion.getRecommendedSchema();
}
String fullName = c.getName();
Schema schema = names.get(fullName);
if (schema == null) {
AvroDoc annotatedDoc = c.getAnnotation(AvroDoc.class); // Docstring
String doc = (annotatedDoc != null) ? annotatedDoc.value() : null;
String name = c.getSimpleName();
String space = c.getPackage() == null ? "" : c.getPackage().getName();
if (c.getEnclosingClass() != null) // nested class
space = c.getEnclosingClass().getName().replace('$', '.');
Union union = c.getAnnotation(Union.class);
if (union != null) { // union annotated
return getAnnotatedUnion(union, names);
} else if (isStringable(c)) { // Stringable
Schema result = Schema.create(Schema.Type.STRING);
result.addProp(CLASS_PROP, c.getName());
return result;
} else if (c.isEnum()) { // Enum
List<String> symbols = new ArrayList<>();
Enum[] constants = (Enum[]) c.getEnumConstants();
for (Enum constant : constants)
symbols.add(constant.name());
schema = Schema.createEnum(name, doc, space, symbols);
consumeAvroAliasAnnotation(c, schema);
} else if (GenericFixed.class.isAssignableFrom(c)) { // fixed
int size = c.getAnnotation(FixedSize.class).value();
schema = Schema.createFixed(name, doc, space, size);
consumeAvroAliasAnnotation(c, schema);
} else if (IndexedRecord.class.isAssignableFrom(c)) { // specific
return super.createSchema(type, names);
} else { // record
List<Schema.Field> fields = new ArrayList<>();
boolean error = Throwable.class.isAssignableFrom(c);
schema = Schema.createRecord(name, doc, space, error);
consumeAvroAliasAnnotation(c, schema);
names.put(c.getName(), schema);
for (Field field : getCachedFields(c))
if ((field.getModifiers() & (Modifier.TRANSIENT | Modifier.STATIC)) == 0
&& !field.isAnnotationPresent(AvroIgnore.class)) {
Schema fieldSchema = createFieldSchema(field, names);
annotatedDoc = field.getAnnotation(AvroDoc.class); // Docstring
doc = (annotatedDoc != null) ? annotatedDoc.value() : null;
Object defaultValue = createSchemaDefaultValue(type, field, fieldSchema);
AvroName annotatedName = field.getAnnotation(AvroName.class); // Rename fields
String fieldName = (annotatedName != null) ? annotatedName.value() : field.getName();
if (STRING_OUTER_PARENT_REFERENCE.equals(fieldName)) {
throw new AvroTypeException("Class " + fullName + " must be a static inner class");
}
Schema.Field recordField = new Schema.Field(fieldName, fieldSchema, doc, defaultValue);
AvroMeta[] metadata = field.getAnnotationsByType(AvroMeta.class); // add metadata
for (AvroMeta meta : metadata) {
if (recordField.propsContainsKey(meta.key())) {
throw new AvroTypeException("Duplicate field prop key: " + meta.key());
}
recordField.addProp(meta.key(), meta.value());
}
for (Schema.Field f : fields) {
if (f.name().equals(fieldName))
throw new AvroTypeException("double field entry: " + fieldName);
}
consumeFieldAlias(field, recordField);
fields.add(recordField);
}
if (error) // add Throwable message
fields.add(new Schema.Field("detailMessage", THROWABLE_MESSAGE, null, null));
schema.setFields(fields);
AvroMeta[] metadata = c.getAnnotationsByType(AvroMeta.class);
for (AvroMeta meta : metadata) {
if (schema.propsContainsKey(meta.key())) {
throw new AvroTypeException("Duplicate type prop key: " + meta.key());
}
schema.addProp(meta.key(), meta.value());
}
}
names.put(fullName, schema);
}
return schema;
}
return super.createSchema(type, names);
}