Exemplo n.º 1
0
  @Override
  public TypeSpec buildTypeSpec() {
    TypeSpec.Builder classBuilder = TypeSpec.classBuilder(schema.getSchemaClassName().simpleName());
    classBuilder.addModifiers(Modifier.PUBLIC);
    classBuilder.addSuperinterface(Types.getSchema(schema.getModelClassName()));

    classBuilder.addFields(buildFieldSpecs());
    classBuilder.addMethods(buildMethodSpecs());

    return classBuilder.build();
  }
  public static DeriveResult<DerivedCodeSpec> derive(
      AlgebraicDataType adt, DeriveContext deriveContext, DeriveUtils deriveUtils) {

    // skip constructors for enums
    if (adt.typeConstructor().declaredType().asElement().getKind() == ElementKind.ENUM) {
      return result(none());
    }

    TypeConstructor typeConstructor = adt.typeConstructor();
    TypeElement lazyTypeElement =
        FlavourImpl.findF0(deriveContext.flavour(), deriveUtils.elements());
    TypeName lazyArgTypeName =
        TypeName.get(
            deriveUtils.types().getDeclaredType(lazyTypeElement, typeConstructor.declaredType()));
    String lazyArgName = Utils.uncapitalize(typeConstructor.typeElement().getSimpleName());
    TypeName typeName = TypeName.get(typeConstructor.declaredType());

    List<TypeVariableName> typeVariableNames =
        adt.typeConstructor()
            .typeVariables()
            .stream()
            .map(TypeVariableName::get)
            .collect(Collectors.toList());

    String className = "Lazy";
    TypeSpec.Builder typeSpecBuilder =
        TypeSpec.classBuilder(className)
            .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
            .addTypeVariables(typeVariableNames)
            .addField(
                FieldSpec.builder(
                        TypeName.get(Object.class), "lock", Modifier.PRIVATE, Modifier.FINAL)
                    .initializer("new Object()")
                    .build())
            .addField(FieldSpec.builder(lazyArgTypeName, "expression", Modifier.PRIVATE).build())
            .addField(
                FieldSpec.builder(typeName, "evaluation", Modifier.PRIVATE, Modifier.VOLATILE)
                    .build())
            .addMethod(
                MethodSpec.constructorBuilder()
                    .addParameter(ParameterSpec.builder(lazyArgTypeName, lazyArgName).build())
                    .addStatement("this.expression = $N", lazyArgName)
                    .build())
            .addMethod(
                MethodSpec.methodBuilder("eval")
                    .addModifiers(Modifier.PRIVATE)
                    .returns(typeName)
                    .addCode(
                        CodeBlock.builder()
                            .addStatement("$T _evaluation = this.evaluation", typeName)
                            .beginControlFlow("if (_evaluation == null)")
                            .beginControlFlow("synchronized (this.lock)")
                            .addStatement("_evaluation = this.evaluation")
                            .beginControlFlow("if (_evaluation == null)")
                            .addStatement(
                                "this.evaluation = _evaluation = expression.$L()",
                                Utils.getAbstractMethods(lazyTypeElement.getEnclosedElements())
                                    .get(0)
                                    .getSimpleName())
                            .addStatement("this.expression = null")
                            .endControlFlow()
                            .endControlFlow()
                            .endControlFlow()
                            .addStatement("return _evaluation")
                            .build())
                    .build())
            .addMethod(
                Utils.overrideMethodBuilder(adt.matchMethod().element())
                    .addStatement(
                        "return this.eval().$L($L)",
                        adt.matchMethod().element().getSimpleName(),
                        Utils.asArgumentsStringOld(adt.matchMethod().element().getParameters()))
                    .build());

    if (adt.typeConstructor().declaredType().asElement().getKind() == ElementKind.INTERFACE) {
      typeSpecBuilder.addSuperinterface(typeName);
    } else {
      typeSpecBuilder.superclass(typeName);
    }

    typeSpecBuilder.addMethods(
        optionalAsStream(
                findAbstractEquals(typeConstructor.typeElement(), deriveUtils.elements())
                    .map(
                        equals ->
                            deriveUtils
                                .overrideMethodBuilder(equals)
                                .addStatement(
                                    "return this.eval().equals($L)",
                                    equals.getParameters().get(0).getSimpleName())
                                .build()))
            .collect(Collectors.toList()));

    typeSpecBuilder.addMethods(
        optionalAsStream(
                findAbstractHashCode(typeConstructor.typeElement(), deriveUtils.elements())
                    .map(
                        hashCode ->
                            deriveUtils
                                .overrideMethodBuilder(hashCode)
                                .addStatement("return this.eval().hashCode()")
                                .build()))
            .collect(Collectors.toList()));

    typeSpecBuilder.addMethods(
        optionalAsStream(
                findAbstractToString(typeConstructor.typeElement(), deriveUtils.elements())
                    .map(
                        toString ->
                            deriveUtils
                                .overrideMethodBuilder(toString)
                                .addStatement("return this.eval().toString()")
                                .build()))
            .collect(Collectors.toList()));

    return result(
        codeSpec(
            typeSpecBuilder.build(),
            MethodSpec.methodBuilder("lazy")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addTypeVariables(
                    typeConstructor
                        .typeVariables()
                        .stream()
                        .map(TypeVariableName::get)
                        .collect(Collectors.toList()))
                .addParameter(lazyArgTypeName, lazyArgName)
                .returns(typeName)
                .addStatement(
                    "return new $L$L($L)",
                    className,
                    typeVariableNames.isEmpty() ? "" : "<>",
                    lazyArgName)
                .build()));
  }