Search in sources :

Example 1 with JsonType

use of com.instagram.common.json.annotation.JsonType in project ig-json-parser by Instagram.

the class JsonAnnotationProcessor method isFieldAnnotationValid.

private boolean isFieldAnnotationValid(Element element) {
    TypeElement classElement = null;
    boolean maybeCheckGetter = false;
    if (element.getKind() == PARAMETER) {
        ExecutableElement constructorElement = (ExecutableElement) element.getEnclosingElement();
        classElement = (TypeElement) constructorElement.getEnclosingElement();
        Annotation jsonType = classElement.getAnnotation(JsonType.class);
        if (jsonType != null && ((JsonType) jsonType).strict()) {
            for (VariableElement variableElement : constructorElement.getParameters()) {
                Annotation annotation = variableElement.getAnnotation(JsonField.class);
                if (annotation == null) {
                    error(constructorElement, "There must be a JsonField annotation for every parameter in %s. The parameter %s does not have one.", constructorElement.getSimpleName(), variableElement.getSimpleName());
                    return false;
                }
            }
        }
        maybeCheckGetter = true;
    } else {
        classElement = (TypeElement) element.getEnclosingElement();
    }
    // Verify containing type.
    if (classElement.getKind() != CLASS) {
        error(classElement, "JsonField field may only be contained in classes. (%s.%s)", classElement.getQualifiedName(), element.getSimpleName());
        return false;
    }
    Annotation annotation = classElement.getAnnotation(JsonType.class);
    if (annotation == null) {
        error(classElement, "JsonField field may only be contained in classes annotated with @JsonType (%s.%s)", classElement.getQualifiedName(), element.getSimpleName());
        return false;
    }
    if (maybeCheckGetter && ((JsonType) annotation).generateSerializer() != JsonType.TriState.NO) {
        boolean isKotlin = isTypeElementKotlin(classElement);
        String getterName = AccessorMetadata.getGetterName(element.getSimpleName().toString(), isKotlin);
        boolean foundGetter = false;
        for (Element enclosedElement : classElement.getEnclosedElements()) {
            if (enclosedElement.getSimpleName().toString().equals(getterName)) {
                foundGetter = true;
            }
        }
        if (!foundGetter) {
            error(classElement, "Found param (%s) annotated with JsonField but expected getter on class %s with name %s.", element.getSimpleName(), classElement.getQualifiedName(), getterName);
            return false;
        }
    }
    // Verify containing class visibility is not private.
    if (classElement.getModifiers().contains(PRIVATE)) {
        error(classElement, "@JsonField %s may not be contained in private classes. (%s.%s)", classElement.getQualifiedName(), element.getSimpleName());
        return false;
    }
    return true;
}
Also used : JsonType(com.instagram.common.json.annotation.JsonType) TypeElement(javax.lang.model.element.TypeElement) ExecutableElement(javax.lang.model.element.ExecutableElement) VariableElement(javax.lang.model.element.VariableElement) TypeElement(javax.lang.model.element.TypeElement) ExecutableElement(javax.lang.model.element.ExecutableElement) Element(javax.lang.model.element.Element) VariableElement(javax.lang.model.element.VariableElement) Annotation(java.lang.annotation.Annotation)

Example 2 with JsonType

use of com.instagram.common.json.annotation.JsonType in project ig-json-parser by Instagram.

the class JsonAnnotationProcessor method processClassAnnotation.

/**
 * This processes a single class that is annotated with {@link JsonType}. It verifies that the
 * class is public and creates an {@link ProcessorClassData} for it.
 */
private void processClassAnnotation(Element element) {
    boolean abstractClass = false;
    TypeElement typeElement = (TypeElement) element;
    // The annotation should be validated for an interface, but no code should be generated.
    JsonType annotation = element.getAnnotation(JsonType.class);
    if (element.getKind() == INTERFACE) {
        return;
    }
    if (annotation.strict()) {
        TypeMirror typeMirror = typeElement.getSuperclass();
        while (typeMirror instanceof DeclaredType) {
            TypeElement parentTypeElement = (TypeElement) ((DeclaredType) typeMirror).asElement();
            if (parentTypeElement.getAnnotation(JsonType.class) != null) {
                error(element, "@JsonType strict=true can not be applied to classes that subclass other JsonType classes. (%s,%s)", typeElement.getQualifiedName(), parentTypeElement.getQualifiedName());
                return;
            }
            typeMirror = parentTypeElement.getSuperclass();
        }
    }
    boolean isKotlin = isTypeElementKotlin(typeElement);
    // Verify containing class visibility is not private.
    if (element.getModifiers().contains(PRIVATE)) {
        error(element, "@JsonType %s may not be applied to private classes. (%s.%s)", typeElement.getQualifiedName(), element.getSimpleName());
        return;
    }
    if (element.getModifiers().contains(ABSTRACT)) {
        abstractClass = true;
    }
    JsonParserClassData injector = mState.mClassElementToInjectorMap.get(typeElement);
    if (injector == null) {
        String parentGeneratedClassName = null;
        if (!mOmitSomeMethodBodies) {
            // Superclass info is only needed if we're generating method bodies.
            TypeMirror superclass = typeElement.getSuperclass();
            // walk up the superclass hierarchy until we find another class we know about.
            while (superclass.getKind() != TypeKind.NONE) {
                TypeElement superclassElement = (TypeElement) mTypes.asElement(superclass);
                if (superclassElement.getAnnotation(JsonType.class) != null) {
                    String superclassPackageName = mTypeUtils.getPackageName(mElements, superclassElement);
                    parentGeneratedClassName = superclassPackageName + "." + mTypeUtils.getPrefixForGeneratedClass(superclassElement, superclassPackageName) + JsonAnnotationProcessorConstants.HELPER_CLASS_SUFFIX;
                    break;
                }
                superclass = superclassElement.getSuperclass();
            }
        }
        boolean generateSerializer = annotation.generateSerializer() == JsonType.TriState.DEFAULT ? mGenerateSerializers : annotation.generateSerializer() == JsonType.TriState.YES;
        String packageName = mTypeUtils.getPackageName(mElements, typeElement);
        injector = new JsonParserClassData(packageName, typeElement.getQualifiedName().toString(), mTypeUtils.getClassName(typeElement, packageName), mTypeUtils.getPrefixForGeneratedClass(typeElement, packageName) + JsonAnnotationProcessorConstants.HELPER_CLASS_SUFFIX, new ProcessorClassData.AnnotationRecordFactory<String, TypeData>() {

            @Override
            public TypeData createAnnotationRecord(String key) {
                return new TypeData();
            }
        }, abstractClass, generateSerializer, mOmitSomeMethodBodies, parentGeneratedClassName, annotation, isKotlin, annotation.strict());
        mState.mClassElementToInjectorMap.put(typeElement, injector);
    }
}
Also used : JsonType(com.instagram.common.json.annotation.JsonType) TypeMirror(javax.lang.model.type.TypeMirror) TypeElement(javax.lang.model.element.TypeElement) DeclaredType(javax.lang.model.type.DeclaredType)

Example 3 with JsonType

use of com.instagram.common.json.annotation.JsonType in project ig-json-parser by Instagram.

the class JsonAnnotationProcessor method processFieldAnnotation.

/**
 * This processes a single field annotated with {@link JsonField}. It locates the enclosing class
 * and then gathers data on the declared type of the field.
 */
private void processFieldAnnotation(Element element) {
    // Verify common generated code restrictions.
    if (!isFieldAnnotationValid(element)) {
        return;
    }
    TypeElement classElement = null;
    if (element.getKind() == PARAMETER) {
        Element constructorElement = (Element) element.getEnclosingElement();
        classElement = (TypeElement) constructorElement.getEnclosingElement();
    } else {
        classElement = (TypeElement) element.getEnclosingElement();
    }
    JsonType jsonTypeAnnotation = classElement.getAnnotation(JsonType.class);
    boolean isStrict = jsonTypeAnnotation.strict();
    boolean isKotlin = isTypeElementKotlin(classElement);
    TypeMirror type = element.asType();
    JsonParserClassData injector = mState.mClassElementToInjectorMap.get(classElement);
    JsonField annotation = element.getAnnotation(JsonField.class);
    TypeData data = injector.getOrCreateRecord(annotation.fieldName().toString());
    boolean isNullable = !isStrict || isFieldElementNullable(element);
    AccessorMetadata accessorMetadata = AccessorMetadata.create(element.getSimpleName().toString(), isStrict, isKotlin, jsonTypeAnnotation.useGetters(), element.getKind());
    if (accessorMetadata.checkMetadataMismatch(data)) {
        error(element, "%s: Detected multiple annotations with the same field name. Field names must be unique within given class.", classElement);
    }
    data.setSerializeType(accessorMetadata.serializeType);
    data.setDeserializeType(accessorMetadata.deserializeType);
    data.setGetterName(accessorMetadata.getterName);
    data.setSetterName(accessorMetadata.setterName);
    data.setMemberVariableName(accessorMetadata.memberVariableName);
    data.setFieldName(annotation.fieldName());
    data.setIsNullable(isNullable);
    data.setAlternateFieldNames(annotation.alternateFieldNames());
    data.setMapping(annotation.mapping());
    data.setValueExtractFormatter(VALUE_EXTRACT.forString(annotation.valueExtractFormatter()));
    data.setAssignmentFormatter(FIELD_ASSIGNMENT.forString(annotation.fieldAssignmentFormatter()));
    data.setSerializeCodeFormatter(FIELD_CODE_SERIALIZATION.forString(annotation.serializeCodeFormatter()));
    TypeUtils.CollectionType collectionType = mTypeUtils.getCollectionType(type);
    data.setCollectionType(collectionType);
    if (collectionType != TypeUtils.CollectionType.NOT_A_COLLECTION) {
        // inspect the inner type.
        type = mTypeUtils.getCollectionParameterizedType(type);
    }
    data.setParseType(mTypeUtils.getParseType(type, JsonType.class));
    boolean skipEnumValidationCheck = setJsonAdapterIfApplicable(type, injector, data, annotation);
    /**
     * UNSUPPORTED can be parsed if valueExtractFormatter and or serializeCodeFormatter have been
     * provided
     */
    if (data.getParseType() == TypeUtils.ParseType.UNSUPPORTED) {
        TypeMirror erasedType = mTypes.erasure(type);
        DeclaredType declaredType = (DeclaredType) erasedType;
        TypeElement typeElement = (TypeElement) declaredType.asElement();
        String packageName = mTypeUtils.getPackageName(mElements, typeElement);
        data.setPackageName(packageName);
        data.setParsableType(mTypeUtils.getClassName(typeElement, packageName));
        CodeFormatter.Factory serializeCodeType = typeElement.getKind() == INTERFACE ? CodeFormatter.CLASS_CODE_SERIALIZATION : CodeFormatter.INTERFACE_CODE_SERIALIZATION;
        data.setIsInterface(typeElement.getKind() == INTERFACE);
        data.setIsWildcard(type != null && type.getKind() == TypeKind.WILDCARD);
    } else if (data.getParseType() == TypeUtils.ParseType.PARSABLE_OBJECT) {
        TypeMirror erasedType = mTypes.erasure(type);
        DeclaredType declaredType = (DeclaredType) erasedType;
        TypeElement typeElement = (TypeElement) declaredType.asElement();
        String packageName = mTypeUtils.getPackageName(mElements, typeElement);
        data.setPackageName(packageName);
        data.setParsableType(mTypeUtils.getClassName(typeElement, packageName));
        data.setParsableTypeParserClass(mTypeUtils.getPrefixForGeneratedClass(typeElement, packageName));
        JsonType typeAnnotation = typeElement.getAnnotation(JsonType.class);
        // Use the parsable object's value extract formatter if existing one is empty
        data.setValueExtractFormatter(data.getValueExtractFormatter().orIfEmpty(VALUE_EXTRACT.forString(typeAnnotation.valueExtractFormatter())));
        CodeFormatter.Factory serializeCodeType = typeElement.getKind() == INTERFACE ? CodeFormatter.CLASS_CODE_SERIALIZATION : CodeFormatter.INTERFACE_CODE_SERIALIZATION;
        data.setSerializeCodeFormatter(data.getSerializeCodeFormatter().orIfEmpty(serializeCodeType.forString(typeAnnotation.serializeCodeFormatter())));
        data.setIsInterface(typeElement.getKind() == INTERFACE);
        data.setIsWildcard(type != null && type.getKind() == TypeKind.WILDCARD);
        data.setFormatterImports(typeAnnotation.typeFormatterImports());
    } else if (data.getParseType() == TypeUtils.ParseType.ENUM_OBJECT) {
        // verify that we have value extract and serializer formatters.
        if (!skipEnumValidationCheck && (StringUtil.isNullOrEmpty(annotation.valueExtractFormatter()) || (injector.generateSerializer() && StringUtil.isNullOrEmpty(annotation.serializeCodeFormatter())))) {
            error(element, "%s: Annotate the enum with @%s (see annotation docs for details). " + "If that is undesirable you must have a value extract formatter, " + "and a serialize code formatter if serialization generation is enabled", classElement, JsonAdapter.class.getSimpleName());
        }
        data.setEnumType(type.toString());
    }
}
Also used : JsonType(com.instagram.common.json.annotation.JsonType) TypeElement(javax.lang.model.element.TypeElement) VariableElement(javax.lang.model.element.VariableElement) TypeElement(javax.lang.model.element.TypeElement) ExecutableElement(javax.lang.model.element.ExecutableElement) Element(javax.lang.model.element.Element) JsonAdapter(com.instagram.common.json.annotation.JsonAdapter) TypeUtils(com.instagram.common.json.annotation.util.TypeUtils) JsonField(com.instagram.common.json.annotation.JsonField) TypeMirror(javax.lang.model.type.TypeMirror) DeclaredType(javax.lang.model.type.DeclaredType)

Aggregations

JsonType (com.instagram.common.json.annotation.JsonType)3 TypeElement (javax.lang.model.element.TypeElement)3 Element (javax.lang.model.element.Element)2 ExecutableElement (javax.lang.model.element.ExecutableElement)2 VariableElement (javax.lang.model.element.VariableElement)2 DeclaredType (javax.lang.model.type.DeclaredType)2 TypeMirror (javax.lang.model.type.TypeMirror)2 JsonAdapter (com.instagram.common.json.annotation.JsonAdapter)1 JsonField (com.instagram.common.json.annotation.JsonField)1 TypeUtils (com.instagram.common.json.annotation.util.TypeUtils)1 Annotation (java.lang.annotation.Annotation)1