
org.openprovenance.prov.template.compiler.CompilerBeanGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of prov-template-compiler Show documentation
Show all versions of prov-template-compiler Show documentation
A template system for PROV bundles.
package org.openprovenance.prov.template.compiler;
import com.squareup.javapoet.*;
import com.squareup.javapoet.PoetParser;
import org.apache.commons.lang3.tuple.Triple;
import org.openprovenance.prov.model.ProvFactory;
import org.openprovenance.prov.template.compiler.common.BeanDirection;
import org.openprovenance.prov.template.compiler.common.BeanKind;
import org.openprovenance.prov.template.compiler.common.Constants;
import org.openprovenance.prov.template.compiler.configuration.*;
import org.openprovenance.prov.template.descriptors.AttributeDescriptor;
import org.openprovenance.prov.template.descriptors.Descriptor;
import org.openprovenance.prov.template.descriptors.NameDescriptor;
import org.openprovenance.prov.template.descriptors.TemplateBindingsSchema;
import org.openprovenance.prov.template.emitter.minilanguage.emitters.Python;
import javax.lang.model.element.Modifier;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static org.openprovenance.prov.template.compiler.ConfigProcessor.typeT;
import static org.openprovenance.prov.template.compiler.ConfigProcessor.*;
public class CompilerBeanGenerator {
public static final String JAVADOC_NO_DOCUMENTATION = "xsd:string";
public static final String PROCESSOR_PARAMETER_NAME = Constants.GENERATED_VAR_PREFIX + "processor";
private final CompilerUtil compilerUtil;
public CompilerBeanGenerator(ProvFactory pFactory) {
this.compilerUtil=new CompilerUtil(pFactory);
}
public SpecificationFile generateBean(TemplatesCompilerConfig configs, Locations locations, String templateName, TemplateBindingsSchema bindingsSchema, BeanKind beanKind, BeanDirection beanDirection, String consistOf, List sharing, String extension, String fileName) {
StackTraceElement stackTraceElement=compilerUtil.thisMethodAndLine();
String name = compilerUtil.beanNameClass(templateName, beanDirection);
if (extension!=null) {
name=name+extension;
}
TypeSpec.Builder builder = compilerUtil.generateClassInit(name);
switch (beanKind) {
case SIMPLE:
builder.addJavadoc("A Simple Bean");
break;
case COMPOSITE:
builder.addJavadoc("A composite Bean");
break;
}
String packge=locations.getFilePackage(beanDirection);
switch (beanDirection) {
case INPUTS:
builder.addJavadoc(" that only contains the input of this template.");
break;
case OUTPUTS:
builder.addJavadoc(" that only contains the outputs of this template.");
break;
case COMMON:
builder.addJavadoc(" that captures all variables of this template.");
break;
default:
throw new IllegalStateException("Unexpected value: " + beanDirection);
}
String directory = locations.convertToDirectory(packge);
if (sharing!=null) {
builder.addJavadoc("\n This includes shared variables $N.", sharing.toString());
}
FieldSpec.Builder b0 = FieldSpec.builder(String.class, Constants.IS_A)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.initializer("$S", templateName);
builder.addField(b0.build());
Map> theVar = bindingsSchema.getVar();
if (beanDirection == BeanDirection.OUTPUTS) {
FieldSpec.Builder b = FieldSpec.builder(Integer.class, "ID");
b.addModifiers(Modifier.PUBLIC);
b.addJavadoc("Allows for database key to be returned.");
builder.addField(b.build());
}
for (String key: descriptorUtils.fieldNames(bindingsSchema)) {
if (beanDirection==BeanDirection.COMMON
|| (beanKind==BeanKind.COMPOSITE)
|| (beanDirection==BeanDirection.OUTPUTS && descriptorUtils.isOutput(key, bindingsSchema))
|| (beanDirection==BeanDirection.INPUTS && (descriptorUtils.isInput(key, bindingsSchema) || sharing!=null && sharing.contains(key)))){
FieldSpec.Builder b = FieldSpec.builder(compilerUtil.getJavaTypeForDeclaredType(theVar, key), key);
b.addModifiers(Modifier.PUBLIC);
Descriptor descriptor=theVar.get(key).get(0);
Function nf=
(nd) -> {
String documentation=nd.getDocumentation()==null? Constants.JAVADOC_NO_DOCUMENTATION : nd.getDocumentation();
String type=nd.getType()==null? JAVADOC_NO_DOCUMENTATION : nd.getType();
b.addJavadoc("$N: $L (expected type: $L)\n", key, documentation, type);
if (sharing!=null && sharing.contains(key))
b.addJavadoc("This is a shared variable in a template composition.\n");
return null;
};
Function af=
(nd) -> {
String documentation=nd.getDocumentation()==null? Constants.JAVADOC_NO_DOCUMENTATION : nd.getDocumentation();
String type=nd.getType()==null? JAVADOC_NO_DOCUMENTATION : nd.getType();
b.addJavadoc("$N: $L (expected type: $L)\n", key, documentation, type);
if (sharing!=null && sharing.contains(key))
b.addJavadoc("This is a shared variable in a template composition.\n ");
return null;
};
descriptorUtils.getFromDescriptor(descriptor,af,nf);
builder.addField(b.build());
}
}
if (beanKind==BeanKind.SIMPLE && beanDirection==BeanDirection.COMMON) {
MethodSpec mbuild = generateInvokeProcessor(templateName, packge, bindingsSchema);
builder.addMethod(mbuild);
} else if (beanKind==BeanKind.COMPOSITE) {
String variant=null;
if (sharing!=null) {
variant = newVariant(consistOf, sharing, configs);
}
generateCompositeList(consistOf, packge, builder, beanDirection, variant, sharing);
generateCompositeListExtender(consistOf, packge, builder, beanDirection, variant, sharing);
}
TypeSpec spec = builder.build();
JavaFile myfile = compilerUtil.specWithComment(spec, templateName, packge, stackTraceElement);
if (locations.python_dir==null) {
return new SpecificationFile(myfile, directory, fileName, packge);
} else {
return newSpecificationFiles(compilerUtil, locations, spec, templateName, stackTraceElement, myfile, directory, fileName, packge, null);
}
}
static public SpecificationFile newSpecificationFiles(CompilerUtil compilerUtil, Locations locations, TypeSpec spec, String templateName, StackTraceElement stackTraceElement, JavaFile myfile, String directory, String fileName, String packge, Set selectedExports) {
return newSpecificationFiles(locations, spec, myfile, directory, fileName, packge, selectedExports, compilerUtil.pySpecWithComment(templateName, stackTraceElement));
}
static public SpecificationFile newSpecificationFiles(CompilerUtil compilerUtil, Locations locations, TypeSpec spec, TemplatesCompilerConfig configs, StackTraceElement stackTraceElement, JavaFile myfile, String directory, String fileName, String packge, Set selectedExports) {
return newSpecificationFiles(locations, spec, myfile, directory, fileName, packge, selectedExports, compilerUtil.pySpecWithComment(configs, stackTraceElement));
}
private static SpecificationFile newSpecificationFiles(Locations locations, TypeSpec spec, JavaFile myfile, String directory, String fileName, String packge, Set selectedExports, String prelude) {
final PoetParser poetParser = new PoetParser();
poetParser.emitPrelude(prelude);
int importPoint=poetParser.getSb().length();
org.openprovenance.prov.template.emitter.minilanguage.Class clazz = poetParser.parse(spec, selectedExports);
Python emitter = new Python(poetParser.getSb(), 0);
clazz.emit(emitter);
// a bit of a trick: defined delayed fields outside the class, after the class definition, this allows the initialiser to refer to class methods.
clazz.emitClassInitialiser(emitter,0);
poetParser.getSb().insert(importPoint,"#end imports\n\n");
for (String imprt: new HashSet<>(emitter.getImports()).stream().sorted().collect(Collectors.toList())) {
poetParser.getSb().insert(importPoint,"\n");
poetParser.getSb().insert(importPoint,imprt);
}
poetParser.getSb().insert(importPoint,"\n\n#start imports\n");
String pyDirectory = locations.python_dir + "/" + packge.replace('.', '/') + "/";
String pyFilename = myfile.typeSpec.name + ".py";
return new SpecificationFile(myfile, directory, fileName, packge,
pyDirectory, pyFilename, () -> poetParser.getSb().toString());
}
public Map, TemplateBindingsSchema>>> variantTable=new HashMap<>();
String newVariant(String templateName, List sharing, TemplatesCompilerConfig configs) {
String shared= stringForSharedVariables(sharing);
variantTable.putIfAbsent(templateName,new HashMap<>());
Triple, TemplateBindingsSchema> triple = variantTable.get(templateName).get(shared);
if (triple ==null) {
String extension = "_" + (variantTable.get(templateName).keySet().size() + 1);
TemplateCompilerConfig config=Arrays.stream(configs.templates).filter(c -> Objects.equals(c.name, templateName)).findFirst().get();
SimpleTemplateCompilerConfig sConfig=(SimpleTemplateCompilerConfig) config;
SimpleTemplateCompilerConfig sConfig2=sConfig.cloneAsInstanceInComposition(templateName+extension);
TemplateBindingsSchema bindingsSchema=compilerUtil.getBindingsSchema(sConfig2);
variantTable.get(templateName).put(shared, Triple.of(extension,sharing,bindingsSchema));
return extension;
} else {
return triple.getLeft();
}
}
private String stringForSharedVariables(List sharing) {
return sharing.stream().sorted().collect(Collectors.joining("_"));
}
static final ParameterizedTypeName classOfUnknown = ParameterizedTypeName.get(ClassName.get(Class.class), TypeVariableName.get("?"));
private void generateCompositeList(String templateName, String packge, TypeSpec.Builder builder, BeanDirection beanDirection, String variant, List sharing) {
String name = compilerUtil.beanNameClass(templateName, beanDirection, variant);
ClassName consistsOfClass = ClassName.get(packge, name);
ParameterizedTypeName elementList=ParameterizedTypeName.get(ClassName.get(List.class), consistsOfClass);
FieldSpec.Builder b1 = FieldSpec.builder(elementList, ELEMENTS)
.addModifiers(Modifier.PUBLIC)
.initializer("new $T<>()", LinkedList.class);
b1.addJavadoc("List of composed templates generated Automatically by ProvToolbox ($N.$N()) for template $N.", this.getClass().getSimpleName(), "generateCompositeList", templateName);
if(variant!=null) {
b1.addJavadoc("\nVariant $N for shared variables $N ($N).", variant, stringForSharedVariables(sharing), sharing.toString());
}
builder.addField(b1.build());
FieldSpec.Builder b2 = FieldSpec.builder(classOfUnknown, "consistsOf")
.addModifiers(Modifier.PUBLIC)
.addJavadoc("Class of elements inside this composite")
.initializer("$T.class", consistsOfClass);
builder.addField(b2.build());
if (variant!=null) {
for (String shared : sharing) {
generateMutatorForSharedVariables(templateName, packge, builder, beanDirection, variant, shared, name);
generateMutatorForDistinctVariables(templateName, packge, builder, beanDirection, variant, shared, name);
}
}
}
private void generateMutatorForDistinctVariables(String templateName, String packge, TypeSpec.Builder builder, BeanDirection beanDirection, String variant, String shared, String name) {
MethodSpec.Builder mbuild = MethodSpec.methodBuilder("distinct" + compilerUtil.capitalize(shared))
.addParameter(ClassName.get(Integer.class), "v")
.addModifiers(Modifier.PUBLIC);
compilerUtil.specWithComment(mbuild);
String countName="__count";
mbuild.addStatement("final int [] $N= { $N }", countName, "v");
mbuild.addStatement("$N.forEach(b -> { b.$N=$N[0]--; })", ELEMENTS, shared, countName);
builder.addMethod(mbuild.build());
}
private void generateMutatorForSharedVariables(String templateName, String packge, TypeSpec.Builder builder, BeanDirection beanDirection, String variant, String shared, String name) {
MethodSpec.Builder mbuild = MethodSpec.methodBuilder("shareAll" + compilerUtil.capitalize(shared))
.addParameter(ClassName.get(Integer.class), "v")
.addModifiers(Modifier.PUBLIC);
compilerUtil.specWithComment(mbuild);
mbuild.addStatement("$N.forEach(b -> { b.$N=$N; })", ELEMENTS, shared, "v");
builder.addMethod(mbuild.build());
}
private void generateCompositeListExtender(String templateName, String packge, TypeSpec.Builder builder, BeanDirection beanDirection, String variant, List sharing) {
String name = compilerUtil.beanNameClass(templateName, beanDirection,variant);
MethodSpec.Builder mbuilder=
MethodSpec.methodBuilder(ADD_ELEMENTS)
.addModifiers(Modifier.PUBLIC)
.addParameter(ClassName.get(Object.class), "o");
compilerUtil.specWithComment(mbuilder);
mbuilder.addStatement("$N.add(($T)o)", ELEMENTS, ClassName.get(packge, name));
builder.addMethod(mbuilder.build());
}
private TypeName processorClassType(String template, String packge) {
return ParameterizedTypeName.get(ClassName.get(packge,compilerUtil.processorNameClass(template)),typeT);
}
public MethodSpec generateInvokeProcessor(String template, String packge, TemplateBindingsSchema bindingsSchema) {
Collection fieldNames = descriptorUtils.fieldNames(bindingsSchema);
if (fieldNames.contains(PROCESSOR_PARAMETER_NAME)) {
throw new IllegalStateException("Template " + template + " contains variable " + PROCESSOR_PARAMETER_NAME + " " + fieldNames);
}
MethodSpec.Builder builder = MethodSpec.methodBuilder(Constants.PROCESSOR_PROCESS_METHOD_NAME)
.addModifiers(Modifier.PUBLIC)
.returns(typeT)
.addTypeVariable(typeT);
builder.addParameter(processorClassType(template,packge), PROCESSOR_PARAMETER_NAME);
builder.addStatement("return $N.$N($L)", PROCESSOR_PARAMETER_NAME, Constants.PROCESSOR_PROCESS_METHOD_NAME,
CodeBlock.join(fieldNames.stream().map(field ->
CodeBlock.of("$N", field)).collect(Collectors.toList()), ","));
return builder.build();
}
public void generateSimpleConfigsWithVariants(Locations locations, TemplatesCompilerConfig configs) {
variantTable.keySet().forEach(
templateName -> {
Map, TemplateBindingsSchema>> allVariants=variantTable.get(templateName);
allVariants.keySet().forEach(
shared -> {
Triple, TemplateBindingsSchema> pair=allVariants.get(shared);
String extension=pair.getLeft();
List sharing=pair.getMiddle();
TemplateBindingsSchema bindingsSchema = pair.getRight();
/*
TemplateCompilerConfig config=Arrays.stream(configs.templates).filter(c -> Objects.equals(c.name, templateName)).findFirst().get();
SimpleTemplateCompilerConfig sConfig=(SimpleTemplateCompilerConfig) config;
SimpleTemplateCompilerConfig sConfig2=sConfig.cloneAsInstanceInComposition(templateName+extension);
TemplateBindingsSchema bindingsSchema=compilerUtil.getBindingsSchema(sConfig2);
allVariantsUpdates.putIfAbsent(templateName,new HashMap<>());
allVariantsUpdates.get(templateName).put(shared,Triple.of(extension, sharing,bindingsSchema));
*/
SpecificationFile spec=generateBean(configs, locations, templateName, bindingsSchema, BeanKind.SIMPLE, BeanDirection.INPUTS, null, sharing, extension, compilerUtil.beanNameClass(templateName,BeanDirection.INPUTS,extension)+DOT_JAVA_EXTENSION);
spec.save();
}
);
}
);
/*
System.out.println("variants Updating with bindings schema ");
allVariantsUpdates.keySet().forEach(
templateName -> allVariantsUpdates.get(templateName).keySet().forEach(
shared -> variantTable.get(templateName).put(shared,allVariantsUpdates.get(templateName).get(shared))
)
);
*/
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy