Java Codegen now supports parametrized ContractIds. (#2621)

Fixes #2258
This commit is contained in:
Gerolf Seitz 2019-08-22 14:01:22 +02:00 committed by mergify[bot]
parent 16d88ebcc0
commit 32a61b275f
7 changed files with 150 additions and 18 deletions

View File

@ -0,0 +1,70 @@
// Copyright (c) 2019 The DAML Authors. 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.Objects;
/**
* This class is used as a super class for all concrete ContractIds generated
* by the java codegen with the following properties:
*
*<pre>
* Foo.ContractId fooCid = new Foo.ContractId("test");
* Bar.ContractId barCid = new Bar.ContractId("test");
* ContractId&lt;Foo&gt; genericFooCid = new ContractId&lt;&gt;("test");
* ContractId&lt;Foo&gt; genericBarCid = new ContractId&lt;&gt;("test");
*
* fooCid.equals(genericFooCid) == true;
* genericFooCid.equals(fooCid) == true;
*
* fooCid.equals(barCid) == false;
* barCid.equals(fooCid) == false;
*</pre>
*
* Due to erase, we cannot distinguish ContractId&lt;Foo&gt; from ContractId&lt;Bar&gt;, thus:
*
* <pre>
* fooCid.equals(genericBarCid) == true
* genericBarCid.equals(fooCid) == true
*
* genericFooCid.equals(genericBarCid) == true
* genericBarCid.equals(genericFooCid) == true
* </pre>
*
* @param <T> A template type
*/
public class ContractId<T> {
public final String contractId;
public ContractId(String contractId) {
this.contractId = contractId;
}
public final Value toValue() {
return new com.daml.ledger.javaapi.data.ContractId(contractId);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(
getClass().isAssignableFrom(o.getClass()) || o.getClass().isAssignableFrom(getClass()))
)
return false;
ContractId<?> that = (ContractId<?>) o;
return contractId.equals(that.contractId);
}
@Override
public int hashCode() {
return Objects.hash(contractId);
}
@Override
public String toString() {
return "ContractId(" + contractId + ')';
}
}

View File

@ -43,4 +43,12 @@ template Foo
with
owner : Party
where
signatory owner
signatory owner
data ParametrizedContractId a = ParametricContractId
with
parametrizedContractId: ContractId a
data FixedContractId = FixedContractId
with
fixedContractId: ParametrizedContractId Foo

View File

@ -14,6 +14,7 @@ import com.digitalasset.AllGenericTests;
OptionalTest.class,
MapTest.class,
ContractKeysTest.class,
ParametrizedContractIdTest.class,
})
public class AllTests {
}

View File

@ -0,0 +1,59 @@
// Copyright (c) 2019 The DAML Authors. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.digitalasset.lf_latest;
import com.daml.ledger.javaapi.data.Record;
import com.daml.ledger.javaapi.data.codegen.ContractId;
import com.digitalasset.ledger.api.v1.ValueOuterClass;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import tests.recordtest.FixedContractId;
import tests.recordtest.Foo;
import tests.recordtest.ParametrizedContractId;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@RunWith(JUnitPlatform.class)
public class ParametrizedContractIdTest {
@Test
void contractIdsCanBeParameterized() {
ValueOuterClass.Record protoRecord = ValueOuterClass.Record.newBuilder()
.addFields(ValueOuterClass.RecordField.newBuilder()
.setLabel("fixedContractId")
.setValue(ValueOuterClass.Value.newBuilder().setRecord(ValueOuterClass.Record.newBuilder()
.addFields(ValueOuterClass.RecordField.newBuilder()
.setLabel("parametrizedContractId")
.setValue(ValueOuterClass.Value.newBuilder().setContractId("SomeID"))
)
)
)
).build();
Record dataRecord = Record.fromProto(protoRecord);
FixedContractId fromValue = FixedContractId.fromValue(dataRecord);
FixedContractId fromConstructor = new FixedContractId(new ParametrizedContractId<>(new Foo.ContractId("SomeID")));
FixedContractId fromRoundTrip = FixedContractId.fromValue(fromConstructor.toValue());
assertEquals(fromValue, fromConstructor);
assertEquals(fromConstructor.toValue(), dataRecord);
assertEquals(fromConstructor.toValue().toProtoRecord(), protoRecord);
assertEquals(fromRoundTrip, fromConstructor);
}
@Test
void fixedContractIdIsEqualToParametrizedContractId() {
Foo.ContractId fixed = new Foo.ContractId("test");
ContractId<Foo> parametrized = new ContractId<>("test");
tests.template1.TestTemplate.ContractId test = new tests.template1.TestTemplate.ContractId("test");
assertEquals(parametrized, fixed);
assertEquals(fixed, parametrized);
assertNotEquals(test, fixed);
assertNotEquals(fixed, test);
}
}

View File

@ -6,7 +6,8 @@ package com.digitalasset.daml.lf.codegen.backend.java.inner
import java.util.Optional
import com.daml.ledger.javaapi
import com.daml.ledger.javaapi.data.{ContractId, CreatedEvent}
import com.daml.ledger.javaapi.data.CreatedEvent
import com.daml.ledger.javaapi.data.codegen.{ContractId => CodegenContractId}
import com.digitalasset.daml.lf.codegen.TypeWithContext
import com.digitalasset.daml.lf.codegen.backend.java.ObjectMethods
import com.digitalasset.daml.lf.data.Ref.{ChoiceName, PackageId, QualifiedName}
@ -301,14 +302,15 @@ private[inner] object TemplateClass extends StrictLogging {
val idClassBuilder =
TypeSpec
.classBuilder("ContractId")
.superclass(ParameterizedTypeName
.get(ClassName.get(classOf[CodegenContractId[_]]), templateClassName))
.addModifiers(Modifier.FINAL, Modifier.PUBLIC, Modifier.STATIC)
.addField(ClassName.get(classOf[String]), "contractId", Modifier.PUBLIC, Modifier.FINAL)
val constructor =
MethodSpec
.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ClassName.get(classOf[String]), "contractId")
.addStatement("this.contractId = contractId")
.addStatement("super(contractId)")
.build()
idClassBuilder.addMethod(constructor)
for ((choiceName, choice) <- choices) {
@ -327,19 +329,6 @@ private[inner] object TemplateClass extends StrictLogging {
idClassBuilder.addMethod(splatted)
}
}
val toValue = MethodSpec
.methodBuilder("toValue")
.addModifiers(Modifier.PUBLIC)
.returns(classOf[javaapi.data.Value])
.addStatement(
CodeBlock.of("return new $L(this.contractId)", ClassName.get(classOf[ContractId])))
.build()
idClassBuilder.addMethod(toValue)
idClassBuilder.addMethods(ObjectMethods(
ClassName.bestGuess("ContractId"),
IndexedSeq("contractId"),
templateClassName).asJava)
idClassBuilder.build()
}

View File

@ -6,6 +6,7 @@ package com.digitalasset.daml.lf.codegen.backend.java
import java.util
import com.daml.ledger.javaapi
import com.daml.ledger.javaapi.data.codegen.ContractId
import com.daml.ledger.javaapi.data.{DamlList, DamlMap, DamlOptional}
import com.digitalasset.daml.lf.data.ImmArray.ImmArraySeq
import com.digitalasset.daml.lf.data.Ref.{Identifier, PackageId, QualifiedName}
@ -59,7 +60,9 @@ package object inner {
case TypePrim(PrimTypeContractId, ImmArraySeq(templateType)) =>
toJavaTypeName(templateType, packagePrefixes) match {
case templateClass: ClassName => templateClass.nestedClass("ContractId")
case _ => sys.error("should not happen")
case typeVariableName: TypeVariableName =>
ParameterizedTypeName.get(ClassName.get(classOf[ContractId[_]]), typeVariableName)
case unexpected => sys.error(s"Unexpected type [$unexpected] for DAML type [$damlType]")
}
case TypePrim(PrimTypeList, typeParameters) =>
ParameterizedTypeName

View File

@ -26,3 +26,5 @@ HEAD — ongoing
+ [DAML Studio] The VSCode extension now has a configuration field for
passing extra arguments to ``damlc ide``.
+ [Sandbox] Fixed a bug that caused the reset service to hang for 10 seconds. See issue `#2549 <https://github.com/digital-asset/daml/issues/2549>`__.
+ [Java Bindings] The Java Codegen now supports parametrized ContractIds.
See `#2258 <https://github.com/digital-asset/daml/issues/2258>`__.