Skip to content

Commit

Permalink
Provide contract choice metadata (#15116)
Browse files Browse the repository at this point in the history
* #13766 initial implementation of choice metadata for templates

* #13766 initial implementation of choice metadata for interfaces

* #13766 Add marker type to ContractTypeCompanion

* #13766 Minor type fix

* Cleanup after merge with main

* Temporary fix for package prefixes

* Format java files, rename Maker -> ContractType in ContractTypeCompanion

* Add documentation and tests

* Complete choice metadata implementation

CHANGELOG_BEGIN
- Add new representation of a choice, ChoiceMetadata.
- Generated templates and interfaces now include fields for each available choice. These fields will have a ChoiceMetadata type and will be called CHOICE_N, where N represents the choices' name.
CHANGELOG_END

* Update docs and cleanup tests
  • Loading branch information
fayi-da authored Oct 7, 2022
1 parent df2dd37 commit d117040
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 24 deletions.
13 changes: 11 additions & 2 deletions docs/source/app-dev/bindings-java/codegen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,20 @@ A file is generated that defines five Java classes and an interface:

.. code-block:: java
:caption: com/acme/templates/Bar.java
:emphasize-lines: 3,18,24,32,40,44
:emphasize-lines: 3,21,27,35,43,47
package com.acme.templates;
public class Bar extends Template {
public static final Identifier TEMPLATE_ID = new Identifier("some-package-id", "Com.Acme.Templates", "Bar");
public static final ChoiceMetadata<Bar, Archive, Unit> CHOICE_Archive =
ChoiceMetadata.create(/* ... */);
public static final ContractCompanion.WithKey<Contract, ContractId, Bar, BarKey> COMPANION =
new ContractCompanion.WithKey<>("com.acme.templates.Bar",
TEMPLATE_ID, ContractId::new, Bar::fromValue, Contract::new, e -> BarKey.fromValue(e));
TEMPLATE_ID, ContractId::new, Bar::fromValue, Contract::new, e -> BarKey.fromValue(e), List.of(CHOICE_Archive));
public final String owner;
public final String name;
Expand Down Expand Up @@ -515,6 +518,12 @@ Effectively it is a class that contains only the inner type ContractId because o
public final class TIf {
public static final Identifier TEMPLATE_ID = new Identifier("94fb4fa48cef1ec7d474ff3d6883a00b2f337666c302ec5e2b87e986da5c27a3", "Interfaces", "TIf");
public static final ChoiceMetadata<TIf, Transfer, ContractId> CHOICE_Transfer =
ChoiceMetadata.create(/* ... */);
public static final ChoiceMetadata<TIf, Archive, Unit> CHOICE_Archive =
ChoiceMetadata.create(/* ... */);
public static final INTERFACE INTERFACE = new INTERFACE();
public static final class ContractId extends com.daml.ledger.javaapi.data.codegen.ContractId<TIf>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.Value;
import java.util.function.Function;

/**
* This represents a Daml choice
*
* @param <Tpl> The generated template class or marker interface for a Daml interface
* @param <ArgType> The choice's argument type
* @param <ResType> The result from exercising the choice
*/
public final class ChoiceMetadata<Tpl, ArgType, ResType> {

/** The choice name * */
public final String name;

private final Function<ArgType, Value> encodeArg;

private ChoiceMetadata(final String name, final Function<ArgType, Value> encodeArg) {
this.name = name;
this.encodeArg = encodeArg;
}

/**
* <strong>INTERNAL API</strong>: this is meant for use by <a
* href="https://docs.daml.com/app-dev/bindings-java/codegen.html">the Java code generator</a>,
* and <em>should not be referenced directly</em>. Applications should refer to the generated
* {@code CHOICE_*} fields on templates or interfaces.
*/
public static <Tpl, ArgType, ResType> ChoiceMetadata<Tpl, ArgType, ResType> create(
final String name, final Function<ArgType, Value> encodeArg) {
return new ChoiceMetadata<>(name, encodeArg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.daml.ledger.javaapi.data.DamlRecord;
import com.daml.ledger.javaapi.data.Identifier;
import com.daml.ledger.javaapi.data.Value;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
Expand Down Expand Up @@ -81,8 +82,9 @@ protected ContractCompanion(
String templateClassName,
Identifier templateId,
Function<String, Id> newContractId,
Function<DamlRecord, Data> fromValue) {
super(templateId);
Function<DamlRecord, Data> fromValue,
List<ChoiceMetadata<Data, ?, ?>> choices) {
super(templateId, choices);
this.templateClassName = templateClassName;
this.newContractId = newContractId;
this.fromValue = fromValue;
Expand All @@ -103,8 +105,9 @@ public WithoutKey(
Identifier templateId,
Function<String, Id> newContractId,
Function<DamlRecord, Data> fromValue,
NewContract<Ct, Id, Data> newContract) {
super(templateClassName, templateId, newContractId, fromValue);
NewContract<Ct, Id, Data> newContract,
List<ChoiceMetadata<Data, ?, ?>> choices) {
super(templateClassName, templateId, newContractId, fromValue, choices);
this.newContract = newContract;
}

Expand Down Expand Up @@ -158,8 +161,9 @@ public WithKey(
Function<String, Id> newContractId,
Function<DamlRecord, Data> fromValue,
NewContract<Ct, Id, Data, Key> newContract,
List<ChoiceMetadata<Data, ?, ?>> choices,
Function<Value, Key> keyFromValue) {
super(templateClassName, templateId, newContractId, fromValue);
super(templateClassName, templateId, newContractId, fromValue, choices);
this.newContract = newContract;
this.keyFromValue = keyFromValue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,38 @@
package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.Identifier;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/** The commonality between {@link ContractCompanion} and {@link InterfaceCompanion}. */
public abstract class ContractTypeCompanion<Maker, Data> {
public abstract class ContractTypeCompanion<ContractType, Data> {
/** The full template ID of the template or interface that defined this companion. */
public final Identifier TEMPLATE_ID;

/**
* The provides a mapping of choice name to Choice.
*
* <pre>
* // if you statically know the name of a choice
* var c1 = Bar.COMPANION.choices.get("Transfer");
* // it is better to retrieve it directly from the generated field
* var c2 = Bar.CHOICE_Transfer;
* </pre>
*/
public final Map<String, ChoiceMetadata<ContractType, ?, ?>> choices;

/**
* <strong>INTERNAL API</strong>: this is meant for use by {@link ContractCompanion} and {@link
* InterfaceCompanion}, and <em>should not be referenced directly</em>. Applications should refer
* to code-generated {@code COMPANION} and {@code INTERFACE} fields specific to the template or
* interface in question instead.
*/
protected ContractTypeCompanion(Identifier templateId) {
protected ContractTypeCompanion(
Identifier templateId, List<ChoiceMetadata<ContractType, ?, ?>> choices) {
TEMPLATE_ID = templateId;
this.choices =
choices.stream().collect(Collectors.toMap(choice -> choice.name, Function.identity()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package com.daml.ledger.javaapi.data.codegen;

import com.daml.ledger.javaapi.data.Identifier;
import java.util.List;

/**
* Metadata and utilities associated with an interface as a whole. Its subclasses serve to
Expand All @@ -23,8 +24,11 @@ public abstract class InterfaceCompanion<I, View> extends ContractTypeCompanion<
* and <em>should not be referenced directly</em>. Applications should refer to the {@code
* INTERFACE} field on generated code for Daml interfaces instead.
*/
protected InterfaceCompanion(Identifier templateId, ValueDecoder<View> valueDecoder) {
super(templateId);
protected InterfaceCompanion(
Identifier templateId,
ValueDecoder<View> valueDecoder,
List<ChoiceMetadata<I, ?, ?>> choices) {
super(templateId, choices);
this.valueDecoder = valueDecoder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ object ContractIdClass {
MethodSpec
.methodBuilder("toInterface")
.addModifiers(Modifier.PUBLIC)
.addParameter(name nestedClass InterfaceClass.companionName, "interfaceCompanion")
.addParameter(name nestedClass InterfaceClass.companionClassName, "interfaceCompanion")
.addStatement("return new $T($L)", interfaceContractIdName, selfArgs)
.returns(interfaceContractIdName)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
package com.daml.lf.codegen.backend.java.inner

import com.daml.ledger.javaapi.data.codegen.InterfaceCompanion
import com.daml.lf.data.Ref.{PackageId, QualifiedName}
import com.daml.lf.typesig, typesig.DefInterface
import com.daml.lf.codegen.backend.java.inner.TemplateClass.toChoiceNameField
import com.daml.lf.data.Ref.{ChoiceName, PackageId, QualifiedName}
import com.daml.lf.typesig
import typesig.DefInterface
import com.squareup.javapoet._
import com.typesafe.scalalogging.StrictLogging
import scalaz.-\/

import javax.lang.model.element.Modifier
import scala.jdk.CollectionConverters._

object InterfaceClass extends StrictLogging {

Expand All @@ -29,6 +32,16 @@ object InterfaceClass extends StrictLogging {
.classBuilder(interfaceName)
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.addField(generateTemplateIdField(packageId, interfaceId))
.addFields(
TemplateClass
.generateChoicesMetadata(
interfaceName,
packagePrefixes,
interface.choices,
withPrefixes = false, // TODO: remove in #15154
)
.asJava
)
.addField(generateInterfaceCompanionField())
.addType(
ContractIdClass
Expand Down Expand Up @@ -57,6 +70,7 @@ object InterfaceClass extends StrictLogging {
.addType(
generateInterfaceCompanionClass(
interfaceName = interfaceName,
choiceNames = interface.choices.keySet,
interfaceViewTypeName = interfaceViewTypeName,
)
)
Expand All @@ -70,24 +84,26 @@ object InterfaceClass extends StrictLogging {
}

private[inner] val companionName = "INTERFACE"
private[inner] val companionClassName = "INTERFACE_"

private def generateInterfaceCompanionField(): FieldSpec =
FieldSpec
.builder(
ClassName bestGuess companionName,
ClassName bestGuess companionClassName,
companionName,
Modifier.FINAL,
Modifier.PUBLIC,
Modifier.STATIC,
)
.initializer("new $N()", companionName)
.initializer("new $N()", companionClassName)
.build()

private def generateInterfaceCompanionClass(
interfaceName: ClassName,
choiceNames: Set[ChoiceName],
interfaceViewTypeName: ClassName,
): TypeSpec = TypeSpec
.classBuilder(companionName)
.classBuilder(companionClassName)
.superclass(
ParameterizedTypeName
.get(ClassName get classOf[InterfaceCompanion[_, _]], interfaceName, interfaceViewTypeName)
Expand All @@ -98,11 +114,19 @@ object InterfaceClass extends StrictLogging {
.constructorBuilder()
// intentionally package-private
.addStatement(
"super($T.$N, $T.$L())",
"super($T.$N, $T.$L(), $T.of($L))",
interfaceName,
ClassGenUtils.templateIdFieldName,
interfaceViewTypeName,
"valueDecoder",
classOf[java.util.List[_]],
CodeBlock
.join(
choiceNames
.map(choiceName => CodeBlock.of("$N", toChoiceNameField(choiceName)))
.asJava,
",$W",
),
)
.build()
}
Expand Down
Loading

0 comments on commit d117040

Please sign in to comment.