All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openprovenance.prov.template.compiler.CompilerBeanGenerator Maven / Gradle / Ivy

There is a newer version: 2.2.1
Show newest version
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