package org.jetbrains.protocolReader; import org.jetbrains.jsonProtocol.JsonField; import org.jetbrains.jsonProtocol.JsonNullable; import org.jetbrains.jsonProtocol.JsonOptionalField; import org.jetbrains.jsonProtocol.JsonSubtypeCasting; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.*; class FieldProcessor { private final Class typeClass; private final List fieldLoaders = new ArrayList<>(2); private final LinkedHashMap methodHandlerMap = new LinkedHashMap<>(); private final List volatileFields = new ArrayList<>(2); boolean lazyRead; private final InterfaceReader reader; FieldProcessor(InterfaceReader reader, Class typeClass) { this.typeClass = typeClass; this.reader = reader; } void go() { Method[] methods = typeClass.getDeclaredMethods(); // todo sort by source location Arrays.sort(methods, new Comparator() { @Override public int compare(Method o1, Method o2) { return o1.getName().compareTo(o2.getName()); } }); for (Method m : methods) { try { processMethod(m); } catch (JsonProtocolModelParseException e) { throw new JsonProtocolModelParseException("Problem with method " + m, e); } } } private void processMethod(Method m) { if (m.getParameterTypes().length != 0) { throw new JsonProtocolModelParseException("No parameters expected in " + m); } String fieldName = checkAndGetJsonFieldName(m); MethodHandler methodHandler; JsonSubtypeCasting jsonSubtypeCaseAnnotation = m.getAnnotation(JsonSubtypeCasting.class); if (jsonSubtypeCaseAnnotation != null) { methodHandler = processManualSubtypeMethod(m, jsonSubtypeCaseAnnotation); lazyRead = true; } else { methodHandler = processFieldGetterMethod(m, fieldName); } methodHandlerMap.put(m, methodHandler); } private MethodHandler processFieldGetterMethod(Method m, String fieldName) { Type genericReturnType = m.getGenericReturnType(); boolean nullable; if (m.getAnnotation(JsonNullable.class) != null) { nullable = true; } else if (genericReturnType == String.class || genericReturnType == Enum.class) { JsonField jsonField = m.getAnnotation(JsonField.class); if (jsonField != null) { nullable = jsonField.optional() && !jsonField.allowAnyPrimitiveValue() && !jsonField.allowAnyPrimitiveValueAndMap(); } else { nullable = m.getAnnotation(JsonOptionalField.class) != null; } } else { nullable = false; } ValueReader fieldTypeParser = reader.getFieldTypeParser(genericReturnType, nullable, false, m); if (fieldTypeParser != InterfaceReader.VOID_PARSER) { fieldLoaders.add(new FieldLoader(fieldName, fieldTypeParser)); } return new PreparsedFieldMethodHandler(fieldTypeParser == InterfaceReader.VOID_PARSER ? null : fieldName); } private MethodHandler processManualSubtypeMethod(final Method m, JsonSubtypeCasting jsonSubtypeCaseAnn) { ValueReader fieldTypeParser = reader.getFieldTypeParser(m.getGenericReturnType(), false, !jsonSubtypeCaseAnn.reinterpret(), null); VolatileFieldBinding fieldInfo = allocateVolatileField(fieldTypeParser, true); LazyCachedMethodHandler handler = new LazyCachedMethodHandler(fieldTypeParser, fieldInfo); ObjectValueReader parserAsObjectValueParser = fieldTypeParser.asJsonTypeParser(); if (parserAsObjectValueParser != null && parserAsObjectValueParser.isSubtyping()) { SubtypeCaster subtypeCaster = new SubtypeCaster(parserAsObjectValueParser.getType()) { @Override void writeJava(TextOutput out) { out.append(m.getName()).append("()"); } }; reader.subtypeCasters.add(subtypeCaster); } return handler; } List getVolatileFields() { return volatileFields; } List getFieldLoaders() { return fieldLoaders; } LinkedHashMap getMethodHandlerMap() { return methodHandlerMap; } private VolatileFieldBinding allocateVolatileField(final ValueReader fieldTypeParser, boolean internalType) { int position = volatileFields.size(); FieldTypeInfo fieldTypeInfo; if (internalType) { fieldTypeInfo = new FieldTypeInfo() { @Override public void appendValueTypeNameJava(FileScope scope, TextOutput out) { fieldTypeParser.appendInternalValueTypeName(scope, out); } }; } else { fieldTypeInfo = new FieldTypeInfo() { @Override public void appendValueTypeNameJava(FileScope scope, TextOutput out) { fieldTypeParser.appendFinishedValueTypeName(out); } }; } VolatileFieldBinding binding = new VolatileFieldBinding(position, fieldTypeInfo); volatileFields.add(binding); return binding; } private static String checkAndGetJsonFieldName(Method m) { if (m.getParameterTypes().length != 0) { throw new JsonProtocolModelParseException("Must have 0 parameters"); } JsonField fieldAnn = m.getAnnotation(JsonField.class); if (fieldAnn != null) { String jsonLiteralName = fieldAnn.jsonLiteralName(); if (!jsonLiteralName.isEmpty()) { return jsonLiteralName; } } return m.getName(); } }