class AutoValueParcelExtension extends AutoValueExtension {

  @Override
  public String generateClass(Context context, String className, String classToExtend,
                              boolean isFinal) {
    ProcessingEnvironment env = context.processingEnvironment();
    TypeName autoValueType =
        TypeName.get(env.getTypeUtils().erasure(context.autoValueClass().asType()));

    ImmutableList<Property> properties = readProperties(context);
    if (!validateProperties(env, properties)) {
      return null;
    }

    ImmutableMap<TypeMirror, FieldSpec> typeAdapters = getTypeAdapters(properties);

    TypeName type = ClassName.get(context.packageName(), className);
    TypeSpec.Builder subclass = TypeSpec.classBuilder(className)
        .addModifiers(FINAL)
        .addMethod(generateConstructor(properties))
        .addMethod(generateWriteToParcel(env, properties, typeAdapters));

    GeneratedAnnotations.generatedAnnotation(env.getElementUtils(), env.getSourceVersion())
        .map(
            annotation ->
                AnnotationSpec.builder(ClassName.get(annotation))
                    .addMember("value", "$S", getClass().getName())
                    .build())
        .ifPresent(subclass::addAnnotation);

    if (!typeAdapters.isEmpty()) {
      for (FieldSpec field : typeAdapters.values()) {
        subclass.addField(field);
      }
    }

    subclass.addField(generateCreator(env, autoValueType, properties, type, typeAdapters));

    ClassName superClass = ClassName.get(context.packageName(), classToExtend);
    List<? extends TypeParameterElement> tpes = context.autoValueClass().getTypeParameters();
    if (tpes.isEmpty()) {
      subclass.superclass(superClass);
    } else {
      TypeName[] superTypeVariables = new TypeName[tpes.size()];
      for (int i = 0, tpesSize = tpes.size(); i < tpesSize; i++) {
        TypeParameterElement tpe = tpes.get(i);
        subclass.addTypeVariable(TypeVariableName.get(tpe));
        superTypeVariables[i] = TypeVariableName.get(tpe.getSimpleName().toString());
      }
      subclass.superclass(ParameterizedTypeName.get(superClass, superTypeVariables));
    }
    if (needsContentDescriptor(context)) {
      subclass.addMethod(generateDescribeContents());
    }

    JavaFile javaFile = JavaFile.builder(context.packageName(), subclass.build()).build();
    return javaFile.toString();
  }

}