1 package org.jetbrains.protocolReader;
3 import org.jetbrains.annotations.NotNull;
4 import org.jetbrains.jsonProtocol.JsonField;
5 import org.jetbrains.jsonProtocol.JsonNullable;
6 import org.jetbrains.jsonProtocol.JsonOptionalField;
7 import org.jetbrains.jsonProtocol.JsonSubtypeCasting;
9 import java.lang.reflect.Method;
10 import java.lang.reflect.Type;
13 final class FieldProcessor<T> {
14 private final List<FieldLoader> fieldLoaders = new ArrayList<>(2);
15 private final LinkedHashMap<Method, MethodHandler> methodHandlerMap = new LinkedHashMap<>();
16 private final List<VolatileFieldBinding> volatileFields = new ArrayList<>(2);
18 private final InterfaceReader reader;
20 FieldProcessor(@NotNull InterfaceReader reader, @NotNull Class<T> typeClass) {
23 Method[] methods = typeClass.getMethods();
24 // todo sort by source location
25 Arrays.sort(methods, new Comparator<Method>() {
27 public int compare(@NotNull Method o1, @NotNull Method o2) {
28 return o1.getName().compareTo(o2.getName());
32 Package aPackage = typeClass.getPackage();
33 for (Method method : methods) {
34 Class<?> methodClass = method.getDeclaringClass();
35 // use method from super if super located in the same package
36 if (methodClass != typeClass && methodClass.getPackage() != aPackage) {
40 if (method.getParameterTypes().length != 0) {
41 throw new JsonProtocolModelParseException("No parameters expected in " + method);
45 String fieldName = checkAndGetJsonFieldName(method);
46 MethodHandler methodHandler;
48 JsonSubtypeCasting jsonSubtypeCaseAnnotation = method.getAnnotation(JsonSubtypeCasting.class);
49 if (jsonSubtypeCaseAnnotation != null) {
50 methodHandler = processManualSubtypeMethod(method, jsonSubtypeCaseAnnotation);
54 methodHandler = processFieldGetterMethod(method, fieldName);
56 methodHandlerMap.put(method, methodHandler);
58 catch (JsonProtocolModelParseException e) {
59 throw new JsonProtocolModelParseException("Problem with method " + method, e);
64 private MethodHandler processFieldGetterMethod(@NotNull Method method, @NotNull String fieldName) {
65 Type genericReturnType = method.getGenericReturnType();
67 if (method.getAnnotation(JsonNullable.class) != null) {
70 else if (genericReturnType == String.class || genericReturnType == Enum.class || (genericReturnType instanceof Class && !((Class)genericReturnType).isPrimitive())) {
71 JsonField jsonField = method.getAnnotation(JsonField.class);
72 if (jsonField != null) {
73 nullable = jsonField.optional() && !jsonField.allowAnyPrimitiveValue() && !jsonField.allowAnyPrimitiveValueAndMap();
76 nullable = method.getAnnotation(JsonOptionalField.class) != null;
83 ValueReader fieldTypeParser = reader.getFieldTypeParser(genericReturnType, nullable, false, method);
84 if (fieldTypeParser != InterfaceReader.VOID_PARSER) {
85 fieldLoaders.add(new FieldLoader(fieldName, fieldTypeParser));
88 final String effectiveFieldName = fieldTypeParser == InterfaceReader.VOID_PARSER ? null : fieldName;
89 return new MethodHandler() {
91 void writeMethodImplementationJava(@NotNull ClassScope scope, @NotNull Method method, @NotNull TextOutput out) {
93 out.append("@NotNull").newLine();
95 writeMethodDeclarationJava(out, method);
97 if (effectiveFieldName != null) {
98 out.append("return ").append(FieldLoader.FIELD_PREFIX).append(effectiveFieldName).semi();
105 private MethodHandler processManualSubtypeMethod(final Method m, JsonSubtypeCasting jsonSubtypeCaseAnn) {
106 ValueReader fieldTypeParser = reader.getFieldTypeParser(m.getGenericReturnType(), false, !jsonSubtypeCaseAnn.reinterpret(), null);
107 VolatileFieldBinding fieldInfo = allocateVolatileField(fieldTypeParser, true);
108 LazyCachedMethodHandler handler = new LazyCachedMethodHandler(fieldTypeParser, fieldInfo);
109 ObjectValueReader<?> parserAsObjectValueParser = fieldTypeParser.asJsonTypeParser();
110 if (parserAsObjectValueParser != null && parserAsObjectValueParser.isSubtyping()) {
111 SubtypeCaster subtypeCaster = new SubtypeCaster(parserAsObjectValueParser.getType()) {
113 void writeJava(TextOutput out) {
114 out.append(m.getName()).append("()");
117 reader.subtypeCasters.add(subtypeCaster);
122 List<VolatileFieldBinding> getVolatileFields() {
123 return volatileFields;
126 List<FieldLoader> getFieldLoaders() {
130 LinkedHashMap<Method, MethodHandler> getMethodHandlerMap() {
131 return methodHandlerMap;
134 private VolatileFieldBinding allocateVolatileField(final ValueReader fieldTypeParser, boolean internalType) {
135 int position = volatileFields.size();
136 FieldTypeInfo fieldTypeInfo;
138 fieldTypeInfo = fieldTypeParser::appendInternalValueTypeName;
141 fieldTypeInfo = (scope, out) -> fieldTypeParser.appendFinishedValueTypeName(out);
143 VolatileFieldBinding binding = new VolatileFieldBinding(position, fieldTypeInfo);
144 volatileFields.add(binding);
149 private static String checkAndGetJsonFieldName(@NotNull Method method) {
150 if (method.getParameterTypes().length != 0) {
151 throw new JsonProtocolModelParseException("Must have 0 parameters");
153 JsonField fieldAnnotation = method.getAnnotation(JsonField.class);
154 if (fieldAnnotation != null) {
155 String jsonLiteralName = fieldAnnotation.jsonLiteralName();
156 if (!jsonLiteralName.isEmpty()) {
157 return jsonLiteralName;
160 return method.getName();