org.kaaproject.kaa.avro.avrogen.compiler.Compiler.java Source code

Java tutorial

Introduction

Here is the source code for org.kaaproject.kaa.avro.avrogen.compiler.Compiler.java

Source

/*
 * Copyright 2014-2016 CyberVision, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.kaaproject.kaa.avro.avrogen.compiler;

import static org.apache.commons.lang.StringUtils.join;

import org.apache.avro.Schema;
import org.apache.avro.Schema.Field;
import org.apache.avro.Schema.Type;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.kaaproject.kaa.avro.avrogen.GenerationContext;
import org.kaaproject.kaa.avro.avrogen.KaaGeneratorException;
import org.kaaproject.kaa.avro.avrogen.StyleUtils;
import org.kaaproject.kaa.avro.avrogen.TypeConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * The type Compiler.
 */
public abstract class Compiler {
    private static final String DIRECTION_PROP = "direction";

    private static final Logger LOG = LoggerFactory.getLogger(Compiler.class);
    protected final Map<Schema, GenerationContext> schemaGenerationQueue;
    private final String generatedSourceName;
    protected VelocityEngine engine;

    protected PrintWriter headerWriter;
    protected PrintWriter sourceWriter;

    protected String namespacePrefix;
    // list of schemas that should be skipped during generation
    protected Set<Schema> generatedSchemas = new HashSet<>();
    private List<Schema> schemas = new ArrayList<>();

    private Compiler(String sourceName) throws KaaGeneratorException {
        this.namespacePrefix = "kaa";
        this.generatedSourceName = sourceName;
        this.schemaGenerationQueue = new LinkedHashMap<>();
        initVelocityEngine();
    }

    /**
     * Instantiates a new Compiler.
     *
     * @param schema     the schema that used to generate source files
     * @param sourceName the name of source file
     * @param hdrS       the stream of header file
     * @param srcS       the stream of source file
     */
    public Compiler(Schema schema, String sourceName, OutputStream hdrS, OutputStream srcS)
            throws KaaGeneratorException {
        this(sourceName);
        this.schemas.add(schema);
        this.headerWriter = new PrintWriter(hdrS);
        this.sourceWriter = new PrintWriter(srcS);
        prepareTemplates(false);
    }

    /**
     * Instantiates a new Compiler.
     *
     * @param schemas    list of schemas that used to generate source files
     * @param sourceName name of source files
     * @param hdrS       stream of the header file
     * @param srcS       stream of the source file
     */
    public Compiler(List<Schema> schemas, String sourceName, OutputStream hdrS, OutputStream srcS)
            throws KaaGeneratorException {
        this(sourceName);
        this.schemas.addAll(schemas);
        this.headerWriter = new PrintWriter(hdrS);
        this.sourceWriter = new PrintWriter(srcS);
        prepareTemplates(false);
    }

    public Compiler(List<Schema> schemas, String sourceName, OutputStream hdrS, OutputStream srcS,
            Set<Schema> generatedSchemas) throws KaaGeneratorException {
        this(schemas, sourceName, hdrS, srcS);
        this.generatedSchemas = new HashSet<>(generatedSchemas);
    }

    /**
     * Instantiates a new Compiler.
     *
     * @param schemaPath path to file that contains schema
     * @param outputPath destination path for generated sources
     * @param sourceName name of source files
     */
    public Compiler(String schemaPath, String outputPath, String sourceName) throws KaaGeneratorException {
        this(sourceName);
        try {
            this.schemas.add(new Schema.Parser().parse(new File(schemaPath)));

            prepareTemplates(true);

            File outputDir = new File(outputPath);
            outputDir.mkdirs();

            String headerPath = outputPath + File.separator + generatedSourceName + ".h";
            String sourcePath = outputPath + File.separator + generatedSourceName + getSourceExtension();

            Files.move(new File(headerTemplateGen()).toPath(), new File(headerPath).toPath(),
                    StandardCopyOption.REPLACE_EXISTING);
            Files.move(new File(sourceTemplateGen()).toPath(), new File(sourcePath).toPath(),
                    StandardCopyOption.REPLACE_EXISTING);

            this.headerWriter = new PrintWriter(new BufferedWriter(new FileWriter(headerPath, true)));
            this.sourceWriter = new PrintWriter(new BufferedWriter(new FileWriter(sourcePath, true)));
        } catch (Exception ex) {
            LOG.error("Failed to create ouput path: ", ex);
            throw new KaaGeneratorException("Failed to create output path: " + ex.toString());
        }
    }

    private void initVelocityEngine() {
        engine = new VelocityEngine();

        engine.addProperty("resource.loader", "class, file");
        engine.addProperty("class.resource.loader.class",
                "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        engine.addProperty("file.resource.loader.class",
                "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
        engine.addProperty("file.resource.loader.path", "/, .");
        engine.setProperty("runtime.references.strict", true);
        engine.setProperty("runtime.log.logsystem.class", "org.apache.velocity.runtime.log.NullLogSystem");
    }

    protected abstract String headerTemplateGen();

    protected abstract String sourceTemplateGen();

    protected abstract String headerTemplate();

    protected abstract String sourceTemplate();

    protected abstract String getSourceExtension();

    private void prepareTemplates(boolean toFile) throws KaaGeneratorException {
        try {
            VelocityContext context = new VelocityContext();
            context.put("headerName", generatedSourceName);

            StringWriter hdrWriter = new StringWriter();
            engine.getTemplate(headerTemplate()).merge(context, hdrWriter);

            StringWriter srcWriter = new StringWriter();
            engine.getTemplate(sourceTemplate()).merge(context, srcWriter);

            if (toFile) {
                writeToFile(hdrWriter, srcWriter);
            } else {
                writeToStream(hdrWriter, srcWriter);
            }
        } catch (Exception ex) {
            LOG.error("Failed to prepare source templates: ", ex);
            throw new KaaGeneratorException("Failed to prepare source templates: " + ex.toString());
        }
    }

    private void writeToStream(StringWriter hdrWriter, StringWriter srcWriter) {
        headerWriter.write(hdrWriter.toString());
        sourceWriter.write(srcWriter.toString());
    }

    private void writeToFile(StringWriter hdrWriter, StringWriter srcWriter) throws Exception {
        FileOutputStream hdrOs = new FileOutputStream(headerTemplateGen());
        hdrOs.write(hdrWriter.toString().getBytes());
        hdrOs.close();

        FileOutputStream srcOs = new FileOutputStream(sourceTemplateGen());
        srcOs.write(srcWriter.toString().getBytes());
        srcOs.close();
    }

    /**
     * Generate source files using the schemas and write them to specified source file.
     */
    public Set<Schema> generate() throws KaaGeneratorException {
        try {
            LOG.debug("Processing schemas: [" + join(schemas, ", ") + "]");
            for (Schema schema : schemas) {

                if (schema.getType() == Type.UNION) {
                    for (Schema s : schema.getTypes()) {
                        addAllSchemasToQueue(s, null);
                    }
                } else {
                    addAllSchemasToQueue(schema, null);
                }
            }

            doGenerate();

            LOG.debug("Sources were successfully generated");
            return schemaGenerationQueue.keySet();
        } catch (Exception ex) {
            LOG.error("Failed to generate C sources: ", ex);
            throw new KaaGeneratorException("Failed to generate sources: " + ex.toString());
        } finally {
            headerWriter.close();
            sourceWriter.close();
        }
    }

    /**
     * Recursively add all unique dependencies of a passed schema and the one to generation queue,
     * that used to generate sources.
     */
    private void addAllSchemasToQueue(Schema schema, GenerationContext context) {
        GenerationContext existingContext = schemaGenerationQueue.get(schema);

        if (existingContext != null) {
            existingContext.updateDirection(context);
            return;
        }

        switch (schema.getType()) {
        case RECORD:
            for (Field f : schema.getFields()) {
                addAllSchemasToQueue(f.schema(),
                        new GenerationContext(schema.getName(), f.name(), schema.getProp(DIRECTION_PROP)));
            }
            schemaGenerationQueue.put(schema, null);
            break;
        case UNION:
            for (Schema branchSchema : schema.getTypes()) {
                addAllSchemasToQueue(branchSchema, context);
            }
            schemaGenerationQueue.put(schema, context);
            break;
        case ARRAY:
            addAllSchemasToQueue(schema.getElementType(), context);
            break;
        case ENUM:
            schemaGenerationQueue.put(schema, null);
            break;
        default:
            break;
        }
    }

    protected abstract void doGenerate();

    protected void processRecord(Schema schema, String headerTemplate, String sourceTemplate) {
        VelocityContext context = new VelocityContext();

        context.put("schema", schema);
        context.put("StyleUtils", StyleUtils.class);
        context.put("TypeConverter", TypeConverter.class);
        context.put("namespacePrefix", namespacePrefix);

        StringWriter hdrWriter = new StringWriter();
        engine.getTemplate(headerTemplate).merge(context, hdrWriter);
        appendResult(hdrWriter.toString(), true);

        StringWriter srcWriter = new StringWriter();
        engine.getTemplate(sourceTemplate).merge(context, srcWriter);
        appendResult(srcWriter.toString(), false);
    }

    protected void processEnum(Schema schema, String template) {
        VelocityContext context = new VelocityContext();

        List<String> symbols = schema.getEnumSymbols();

        context.put("schema", schema);
        context.put("symbols", symbols);
        context.put("StyleUtils", StyleUtils.class);
        context.put("namespacePrefix", namespacePrefix);

        StringWriter writer = new StringWriter();
        engine.getTemplate(template).merge(context, writer);
        appendResult(writer.toString(), true);
    }

    protected void appendResult(String str, boolean toHeader) {
        if (toHeader) {
            headerWriter.write(str);
        } else {
            sourceWriter.write(str);
        }
    }

    public void setNamespacePrefix(String namespacePrefix) {
        this.namespacePrefix = namespacePrefix;
    }

}