org.apache.streams.plugins.StreamsScalaSourceGenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.streams.plugins.StreamsScalaSourceGenerator.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.streams.plugins;

import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ConfigurationBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Embed within your own java code
 *
 * <p/>
 * StreamsScalaGenerationConfig config = new StreamsScalaGenerationConfig();
 * config.setTargetDirectory("target/generated-sources/scala");
 * config.setTargetPackage("com.example");
 * StreamsScalaSourceGenerator generator = new StreamsScalaSourceGenerator(config);
 * generator.run();
 *
 */
public class StreamsScalaSourceGenerator implements Runnable {

    private static final Logger LOGGER = LoggerFactory.getLogger(StreamsScalaSourceGenerator.class);

    private static final String LS = System.getProperty("line.separator");

    private StreamsScalaGenerationConfig config;

    private Reflections reflections;

    private String outDir;

    /**
     * Run from CLI without Maven
     *
     * <p/>
     * java -jar streams-plugin-scala-jar-with-dependencies.jar StreamsScalaSourceGenerator target/generated-sources
     *
     * @param args [targetDirectory, targetPackage]
     * */
    public static void main(String[] args) {
        StreamsScalaGenerationConfig config = new StreamsScalaGenerationConfig();

        List<String> sourcePackages = new ArrayList<>();
        String targetDirectory = "target/generated-sources/pojo";
        String targetPackage = "";

        if (args.length > 0) {
            sourcePackages = Stream.of(args[0].split(",")).collect(Collectors.toList());
        }
        if (args.length > 1) {
            targetDirectory = args[1];
        }
        if (args.length > 2) {
            targetPackage = args[2];
        }

        config.setSourcePackages(sourcePackages);
        config.setTargetPackage(targetPackage);
        config.setTargetDirectory(targetDirectory);

        StreamsScalaSourceGenerator streamsScalaSourceGenerator = new StreamsScalaSourceGenerator(config);
        streamsScalaSourceGenerator.run();
    }

    /**
     * StreamsScalaSourceGenerator constructor.
     * @param config StreamsScalaGenerationConfig
     */
    public StreamsScalaSourceGenerator(StreamsScalaGenerationConfig config) {
        this.config = config;
        this.outDir = config.getTargetDirectory().getAbsolutePath();
        reflections = new Reflections(new ConfigurationBuilder()
                // TODO
                .forPackages(config.getSourcePackages().toArray(new String[config.getSourcePackages().size()]))
                .setScanners(new SubTypesScanner(), new TypeAnnotationsScanner()));

    }

    @Override
    public void run() {

        List<Class<?>> serializableClasses = detectSerializableClasses();

        LOGGER.info("Detected {} serialiables:", serializableClasses.size());
        for (Class clazz : serializableClasses) {
            LOGGER.debug(clazz.toString());
        }

        List<Class<?>> pojoClasses = detectPojoClasses(serializableClasses);

        LOGGER.info("Detected {} pojos:", pojoClasses.size());
        for (Class clazz : pojoClasses) {
            LOGGER.debug(clazz.toString());
        }

        List<Class<?>> traits = detectTraits(pojoClasses);

        LOGGER.info("Detected {} traits:", traits.size());
        for (Class clazz : traits) {
            LOGGER.debug(clazz.toString());
        }

        List<Class<?>> cases = detectCases(pojoClasses);

        LOGGER.info("Detected {} cases:", cases.size());
        for (Class clazz : cases) {
            LOGGER.debug(clazz.toString());
        }

        for (Class clazz : traits) {
            String pojoPath = clazz.getPackage().getName().replace(".pojo.json", ".scala").replace(".", "/")
                    + "/traits/";
            String pojoName = clazz.getSimpleName() + ".scala";
            String pojoScala = renderTrait(clazz);
            writeFile(outDir + "/" + pojoPath + pojoName, pojoScala);
        }

        for (Class clazz : traits) {
            String pojoPath = clazz.getPackage().getName().replace(".pojo.json", ".scala").replace(".", "/") + "/";
            String pojoName = clazz.getSimpleName() + ".scala";
            String pojoScala = renderClass(clazz);
            writeFile(outDir + "/" + pojoPath + pojoName, pojoScala);
        }

        for (Class clazz : cases) {
            String pojoPath = clazz.getPackage().getName().replace(".pojo.json", ".scala").replace(".", "/") + "/";
            String pojoName = clazz.getSimpleName() + ".scala";
            String pojoScala = renderCase(clazz);
            writeFile(outDir + "/" + pojoPath + pojoName, pojoScala);
        }

    }

    private void writeFile(String pojoFile, String pojoScala) {
        try {
            File path = new File(pojoFile);
            File dir = path.getParentFile();
            if (!dir.exists()) {
                dir.mkdirs();
            }
            Files.write(Paths.get(pojoFile), pojoScala.getBytes(), StandardOpenOption.CREATE_NEW);
        } catch (Exception ex) {
            LOGGER.error("Write Exception: {}", ex);
        }
    }

    /**
     * detectSerializableClasses.
     * @return List of Serializable Classes
     */
    public List<Class<?>> detectSerializableClasses() {

        Set<Class<? extends Serializable>> classes = reflections.getSubTypesOf(java.io.Serializable.class);

        List<Class<?>> result = new ArrayList<>();

        for (Class clazz : classes) {
            result.add(clazz);
        }

        return result;
    }

    /**
     * detect which Classes are Pojo Classes.
     * @param classes List of candidate Pojo Classes
     * @return List of actual Pojo Classes
     */
    public List<Class<?>> detectPojoClasses(List<Class<?>> classes) {

        List<Class<?>> result = new ArrayList<>();

        for (Class clazz : classes) {
            try {
                clazz.newInstance().toString();
            } catch (Exception ex) {
                //
            }
            // super-halfass way to know if this is a jsonschema2pojo
            if (clazz.getAnnotations().length >= 1) {
                result.add(clazz);
            }
        }

        return result;
    }

    private List<Class<?>> detectTraits(List<Class<?>> classes) {

        List<Class<?>> traits = new ArrayList<>();

        for (Class clazz : classes) {
            if (reflections.getSubTypesOf(clazz).size() > 0) {
                traits.add(clazz);
            }
        }

        return traits;
    }

    private List<Class<?>> detectCases(List<Class<?>> classes) {

        List<Class<?>> cases = new ArrayList<>();

        for (Class clazz : classes) {
            if (reflections.getSubTypesOf(clazz).size() == 0) {
                cases.add(clazz);
            }
        }

        return cases;
    }

    private String renderTrait(Class<?> pojoClass) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("package ");
        stringBuffer.append(pojoClass.getPackage().getName().replace(".pojo.json", ".scala"));
        stringBuffer.append(".traits");
        stringBuffer.append(LS);
        stringBuffer.append("trait ").append(pojoClass.getSimpleName());
        stringBuffer.append(" extends Serializable");
        stringBuffer.append(" {");

        Set<Field> fields = ReflectionUtils.getAllFields(pojoClass);
        appendFields(stringBuffer, fields, "def", ";");

        stringBuffer.append("}");

        return stringBuffer.toString();
    }

    private String renderClass(Class<?> pojoClass) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("package ");
        stringBuffer.append(pojoClass.getPackage().getName().replace(".pojo.json", ".scala"));
        stringBuffer.append(LS);
        stringBuffer
                .append("import org.apache.commons.lang.builder.{HashCodeBuilder, EqualsBuilder, ToStringBuilder}");
        stringBuffer.append(LS);
        stringBuffer.append("class ").append(pojoClass.getSimpleName());
        stringBuffer.append(" (");

        Set<Field> fields = ReflectionUtils.getAllFields(pojoClass);
        appendFields(stringBuffer, fields, "var", ",");

        stringBuffer.append(")");
        stringBuffer.append(" extends ").append(pojoClass.getPackage().getName().replace(".pojo.json", ".scala"))
                .append(".traits.").append(pojoClass.getSimpleName());
        stringBuffer.append(" with Serializable ");
        stringBuffer.append("{ ");
        stringBuffer.append(LS);
        stringBuffer.append("override def equals(obj: Any) = obj match { ");
        stringBuffer.append(LS);
        stringBuffer.append("  case other: ");
        stringBuffer.append(pojoClass.getSimpleName());
        stringBuffer.append(" => other.getClass == getClass && EqualsBuilder.reflectionEquals(this,obj)");
        stringBuffer.append(LS);
        stringBuffer.append("  case _ => false");
        stringBuffer.append(LS);
        stringBuffer.append("}");
        stringBuffer.append(LS);
        stringBuffer.append("override def hashCode = new HashCodeBuilder().hashCode");
        stringBuffer.append(LS);
        stringBuffer.append("}");

        return stringBuffer.toString();
    }

    private String renderCase(Class<?> pojoClass) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("package ");
        stringBuffer.append(pojoClass.getPackage().getName().replace(".pojo.json", ".scala"));
        stringBuffer.append(LS);
        stringBuffer.append("case class " + pojoClass.getSimpleName());
        stringBuffer.append("(");
        Set<Field> fields = ReflectionUtils.getAllFields(pojoClass);
        appendFields(stringBuffer, fields, "var", ",");
        stringBuffer.append(")");
        if (pojoClass.getSuperclass() != null && !pojoClass.getSuperclass().equals(java.lang.Object.class)) {
            stringBuffer.append(
                    " extends " + pojoClass.getSuperclass().getPackage().getName().replace(".pojo.json", ".scala")
                            + ".traits." + pojoClass.getSuperclass().getSimpleName());
        }
        stringBuffer.append(LS);

        return stringBuffer.toString();
    }

    private void appendFields(StringBuffer stringBuffer, Set<Field> fields, String varDef, String fieldDelimiter) {
        if (fields.size() > 0) {
            stringBuffer.append(LS);
            Map<String, Field> fieldsToAppend = uniqueFields(fields);
            for (Iterator<Field> iter = fieldsToAppend.values().iterator(); iter.hasNext();) {
                Field field = iter.next();
                if (override(field)) {
                    stringBuffer.append("override ");
                }
                stringBuffer.append(varDef);
                stringBuffer.append(" ");
                stringBuffer.append(name(field));
                stringBuffer.append(": ");
                if (option(field)) {
                    stringBuffer.append("scala.Option[");
                    stringBuffer.append(type(field));
                    stringBuffer.append("]");
                } else {
                    stringBuffer.append(type(field));
                }
                if (!fieldDelimiter.equals(";") && value(field) != null) {
                    stringBuffer.append(" = ");
                    if (option(field)) {
                        stringBuffer.append("scala.Some(");
                        stringBuffer.append(value(field));
                        stringBuffer.append(")");
                    } else {
                        stringBuffer.append(value(field));
                    }
                }
                if (iter.hasNext()) {
                    stringBuffer.append(fieldDelimiter);
                }
                stringBuffer.append(LS);
            }
        } else {
            stringBuffer.append(LS);
        }
    }

    private boolean option(Field field) {
        return !field.getName().equals("verb") && !field.getType().equals(Map.class)
                && !field.getType().equals(List.class);
    }

    private String value(Field field) {
        switch (field.getName()) {
        case "verb":
            return "\"post\"";
        case "objectType":
            return "\"application\"";
        default:
            return null;
        }
    }

    private String type(Field field) {
        if (field.getType().equals(java.lang.String.class)) {
            return "String";
        } else if (field.getType().equals(java.util.Map.class)) {
            return "scala.collection.mutable.Map[String,Any]";
        } else if (field.getType().equals(java.util.List.class)) {
            return "scala.collection.mutable.MutableList[Any]";
        }
        return field.getType().getCanonicalName().replace(".pojo.json", ".scala");
    }

    private Map<String, Field> uniqueFields(Set<Field> fieldset) {
        Map<String, Field> fields = new TreeMap<>();
        Field item = null;
        for (Iterator<Field> it = fieldset.iterator(); it.hasNext(); item = it.next()) {
            if (item != null && item.getName() != null) {
                Field added = fields.put(item.getName(), item);
            }
            // ensure right class will get used
        }
        return fields;
    }

    private String name(Field field) {
        if (field.getName().equals("object")) {
            return "obj";
        } else {
            return field.getName();
        }
    }

    private boolean override(Field field) {
        try {
            return field.getDeclaringClass().getSuperclass().getField(field.getName()) != null;
        } catch (Exception ex) {
            return false;
        }
    }
}