Skip to content

Commit

Permalink
369 generate pojos for annoation processor from cip57 plutusblueprints (
Browse files Browse the repository at this point in the history
#376)

* #369 added json models and tests for validation

* #369 added parsing of CIP57 Blueprints. Currenlty implemented datatypes: List, Constr, bytes, integer, String, boolean
ToDo: Map, tuple, unit

* #369 add Mapdatatype

* #369 added scriptaddress method and alternatives will be set in converter according to used type

* #369 fixed test deployment

* #369 had to add resources to sourceSets to enable the filer to find the files used in the tests

* #369 still excluding guava

* #369 added AnyPlutusData datatype.
This field is identified through missing datatype description it can be anything. The correctness will be checked by the smartcontract itself. So it can't be distinguished within the blueprint.

* chore: #369 Fixed typo

* chore: #369 Remove test classes

* Fix: Fix camecase class name, package refactoring and target package folder

---------

Co-authored-by: Satya <satran004@gmail.com>
  • Loading branch information
Kammerlo and satran004 authored Jul 17, 2024
1 parent 3bd718a commit 7a07ace
Show file tree
Hide file tree
Showing 44 changed files with 1,780 additions and 30 deletions.
16 changes: 15 additions & 1 deletion annotation-processor/build.gradle
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
dependencies {
api project(':plutus')
implementation(libs.google.auto.service) {
api project(':address')

implementation(libs.google.auto.service){
exclude group: 'com.google.guava', module: 'guava'
}
api libs.javapoet
api libs.apache.common.text

testImplementation libs.google.testing.compile
testImplementation libs.lombok

testAnnotationProcessor project(':annotation-processor')
}

sourceSets {
json {
resources {
srcDir('src/test/resources/')
}
}
test {
compileClasspath += sourceSets.json.output
}
}

publishing {
publications {
mavenJava(MavenPublication) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.bloxbean.cardano.client.plutus.annotation.PlutusIgnore;
import com.bloxbean.cardano.client.plutus.annotation.processor.exception.NotSupportedException;
import com.bloxbean.cardano.client.plutus.annotation.processor.model.*;
import com.bloxbean.cardano.client.plutus.spec.PlutusData;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
Expand Down Expand Up @@ -75,6 +76,7 @@ public ClassDefinition getClassDefinition(TypeElement typeElement) {
VariableElement variableElement = (VariableElement) enclosedElement;
String fieldName = variableElement.getSimpleName().toString();
field.setName(fieldName);
field.setAlternative(getAlternative(fieldName));

ExecutableElement getter = findGetter(typeElement, variableElement);
ExecutableElement setter = findSetter(typeElement, variableElement);
Expand Down Expand Up @@ -123,6 +125,16 @@ public ClassDefinition getClassDefinition(TypeElement typeElement) {
return classDefinition;
}

private int getAlternative(String fieldName) {
Optional<TypeElement> first = typeElements.stream().filter(typeElement -> typeElement.getSimpleName().toString().toLowerCase().equals(fieldName.toLowerCase())).findFirst();
if(first.isPresent()) {
TypeElement typeElement = first.get();
return typeElement.getAnnotation(Constr.class).alternative();
} else {
return 0;
}
}

private FieldType detectFieldType(TypeName typeName, TypeMirror typeMirror) throws NotSupportedException {
FieldType fieldType = new FieldType();
fieldType.setFqTypeName(typeName.toString());
Expand Down Expand Up @@ -153,6 +165,9 @@ private FieldType detectFieldType(TypeName typeName, TypeMirror typeMirror) thro
} else if (typeName.equals(TypeName.BOOLEAN)) {
fieldType.setType(Type.BOOL);
fieldType.setJavaType(JavaType.BOOLEAN);
} else if (typeName.equals(TypeName.get(PlutusData.class))) {
fieldType.setType(Type.PLUTUSDATA);
fieldType.setJavaType(JavaType.PLUTUSDATA);
} else if (typeName instanceof ParameterizedTypeName &&
(((ParameterizedTypeName) typeName).rawType.equals(ClassName.get(List.class))
|| isAssignableToList(typeMirror))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
JavaFile javaFile = JavaFile.builder(classDefinition.getPackageName(), typeSpec)
.build();

String fullClassName = classDefinition.getPackageName() + "." + classDefinition.getName();

JavaFileObject builderFile = processingEnv.getFiler()
.createSourceFile(classDefinition.getName());
.createSourceFile(fullClassName);
Writer writer = builderFile.openWriter();
javaFile.writeTo(writer);
writer.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ private MethodSpec generateToPlutusDataMethod(ClassDefinition classDef) {
.add("\n")
.build();
break;
case PLUTUSDATA:
codeBlock = CodeBlock.builder()
.add("//Field $L\n", field.getName())
.add(nullCheckStatement(field, fieldOrGetterName(field)))
.addStatement("constr.getData().add(obj.$L)", fieldOrGetterName(field))
.add("\n")
.build();
break;
case OPTIONAL:
/*** Sample Optional Code
if (obj.isEmpty())
Expand All @@ -196,9 +204,11 @@ private MethodSpec generateToPlutusDataMethod(ClassDefinition classDef) {
case CONSTRUCTOR:
codeBlock = CodeBlock.builder()
.add("//Field $L\n", field.getName())
.add(nullCheckStatement(field, fieldOrGetterName(field)))
.addStatement("constr.getData().add(new $LConverter().toPlutusData(obj.$L))", field.getFieldType().getJavaType().getName(), fieldOrGetterName(field))
.add("\n")
.beginControlFlow("if(obj.$L != null)", fieldOrGetterName(field))
.addStatement("constr.getData().add(new $LConverter().toPlutusData(obj.$L))", field.getFieldType().getJavaType().getName(), fieldOrGetterName(field))
.addStatement("// Setting the alternative to the childs alternative to use this constructor")
.addStatement("constr = $T.builder().alternative($L).data(constr.getData()).build()", ConstrPlutusData.class, field.getAlternative())
.endControlFlow()
.build();
break;
case BOOL:
Expand Down Expand Up @@ -553,6 +563,12 @@ private CodeBlock getDeserializeCodeBlockForField(Field field) {
.add(generateMapDeserializeCode(field.getFieldType(), field.getName(), field.getName() + "Map", "entry"))
.build();
break;
case PLUTUSDATA:
codeBlock = CodeBlock.builder()
.add("//Field $L\n", field.getName())
.add("var $L = data.getPlutusDataList().get($L);\n", field.getName(), field.getIndex())
.build();
break;
case OPTIONAL:
// var optConstr = (ConstrPlutusData)data.getPlutusDataList().get(2);
// if (optConstr.getAlternative() == 1) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.bloxbean.cardano.client.plutus.annotation.processor.blueprint;

import com.bloxbean.cardano.client.plutus.annotation.Blueprint;
import com.bloxbean.cardano.client.plutus.blueprint.PlutusBlueprintLoader;
import com.bloxbean.cardano.client.plutus.blueprint.model.*;
import com.google.auto.service.AutoService;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import java.io.File;
import java.util.*;

@AutoService(Processor.class)
@Slf4j
public class BlueprintAnnotationProcessor extends AbstractProcessor {

private Messager messager;
private List<TypeElement> typeElements = new ArrayList<>();
private ValidatorProcessor validatorProcessor;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
messager = processingEnv.getMessager();
}

@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotataions = new LinkedHashSet<String>();
annotataions.add(Blueprint.class.getCanonicalName());
return annotataions;
}

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
log.debug("Processing Blueprint annotation");

typeElements = getTypeElementsWithAnnotations(annotations, roundEnv);

for(TypeElement typeElement : typeElements) {
Blueprint annotation = typeElement.getAnnotation(Blueprint.class);
if (annotation == null) {
log.error("Blueprint annotation not found for class {}", typeElement.getSimpleName());
return false;
} else {
validatorProcessor = new ValidatorProcessor(annotation, processingEnv);
}
File blueprintFile = getFileFromAnnotation(annotation);
if (blueprintFile == null || !blueprintFile.exists()) {
log.error("Blueprint file {} not found", annotation.fileInResources());
return false;
}
PlutusContractBlueprint plutusContractBlueprint;
try {
plutusContractBlueprint = PlutusBlueprintLoader.loadBlueprint(blueprintFile);
} catch (Exception e) {
log.error("Error processing blueprint file {}", blueprintFile.getAbsolutePath(), e);
return false;
}
for (Validator validator : plutusContractBlueprint.getValidators()) {
validatorProcessor.processValidator(validator);
}
}
return true;
}

private List<TypeElement> getTypeElementsWithAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
List<TypeElement> elementsList = new ArrayList<>();
for (TypeElement annotation : annotations) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(annotation);
for (Element element : elements) {
if (element instanceof TypeElement) {
TypeElement typeElement = (TypeElement) element;
elementsList.add(typeElement);

}
}
}
return elementsList;
}

private File getFileFromAnnotation(Blueprint annotation) {
File blueprintFile = null;
if(!annotation.file().isEmpty())
blueprintFile = new File(annotation.file());
if(!annotation.fileInResources().isEmpty())
blueprintFile = getFileFromRessourcers(annotation.fileInResources());
if(blueprintFile == null || !blueprintFile.exists()) {
log.error("Blueprint file {} not found", annotation.file());
return null;
}
return blueprintFile;
}

public File getFileFromRessourcers(String s) {
try {
FileObject resource = processingEnv.getFiler().getResource(StandardLocation.CLASS_PATH, "", s);
return new File(resource.toUri());
} catch (Exception e) {
return null;
}
}


private void error(Element e, String msg, Object... args) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(msg, args),
e);
}
}
Loading

0 comments on commit 7a07ace

Please sign in to comment.