@Test
  public void testCreateParameters_indexed() {
    TestBean bean = new TestBean(1L, "foo");
    ParameterSpec builder = new ParameterSpec<TestBean>(TestPage.class, "id", "name");

    PageParameters params = builder.createParameters(bean);
    Assert.assertEquals(bean.getId(), params.get("id").toLongObject());
    Assert.assertEquals(bean.getName(), params.get("name").toString());
  }
  /** Asserts that missing parameter values are gracefully ignored. */
  @Test
  public void testParseParameters_missing() {
    ParameterSpec builder = new ParameterSpec<TestBean>(TestPage.class, "id", "name");

    TestBean bean = new TestBean();
    PageParameters params = new PageParameters();

    builder.parseParameters(params, bean);

    Assert.assertNull(bean.getId());
    Assert.assertNull(bean.getName());
  }
  /**
   * Asserts that badly formatted page parameter value is ignored when {@code throw404OnParseError}
   * is {@code false}.
   */
  @Test
  public void testParseParameters_badformatnull() {
    ParameterSpec builder = new ParameterSpec<TestBean>(TestPage.class, "id", "name");

    TestBean bean = new TestBean();
    PageParameters params = new PageParameters();
    params.set("id", "notparseableaslong");

    builder.parseParameters(params, bean, false);

    Assert.assertNull(bean.getId());
  }
  @Test
  public void testParseParameters_indexed() {
    ParameterSpec builder = new ParameterSpec<TestBean>(TestPage.class, "id", "name");

    TestBean bean = new TestBean();
    PageParameters params = new PageParameters();
    params.set("id", "5");
    params.set("name", "hello");

    builder.parseParameters(params, bean);

    Assert.assertEquals((Long) 5L, (Long) bean.getId());
    Assert.assertEquals("hello", bean.getName());
  }
  /** Asserts that badly formatted page parameter value causes an abort with 404. */
  @Test
  public void testParseParameters_badformat404() {
    ParameterSpec builder = new ParameterSpec<TestBean>(TestPage.class, "id", "name");

    TestBean bean = new TestBean();
    PageParameters params = new PageParameters();
    params.set("id", "notparseableaslong");

    try {
      builder.parseParameters(params, bean);
      Assert.fail("AbortWithHttpErrorCodeException was not thrown.");
    } catch (AbortWithHttpErrorCodeException awhece) {
      Assert.assertEquals(404, awhece.getErrorCode());
    }
  }
  @Test
  public void testCreateLink_indexed() {
    TestBean bean = new TestBean(1L, "foo");
    ParameterSpec builder = new ParameterSpec<TestBean>(TestPage.class, "id", "name");

    BookmarkablePageLink link = builder.createLink("bar", new Model(bean));
    this.tester.startComponent(link);

    Assert.assertEquals("bar", link.getId());
    Assert.assertEquals(TestPage.class, link.getPageClass());

    PageParameters params = link.getPageParameters();
    Assert.assertEquals(bean.getId(), params.get("id").toLongObject());
    Assert.assertEquals(bean.getName(), params.get("name").toString());
  }
  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()));
  }