com.codenvy.dto.generator.DtoGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.codenvy.dto.generator.DtoGenerator.java

Source

// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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 com.codenvy.dto.generator;

import com.codenvy.dto.server.DtoFactoryVisitor;
import com.codenvy.dto.shared.DTO;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hashing;

import org.reflections.Configuration;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/** Simple source generator that takes in the packages list with interface definitions and generates client and server DTO impls. */
public class DtoGenerator {

    private static final String INVALID_PATH = "invalid path";

    private static final String SERVER = "server";

    private static final String CLIENT = "client";

    /** Flag: location of the packages that contains dto interfaces. */
    private String[] dtoPackages = null;

    /** Flag: Name of the generated java class file that contains the DTOs. */
    private String genFileName = "DataObjects.java";

    /** Flag: The type of impls to be generated, either CLIENT or SERVER. */
    private String impl = CLIENT;

    /** Flag: A pattern we can use to search an absolute path and find the start of the package definition.") */
    private String packageBase = "java.";

    public String[] getDtoPackages() {
        return dtoPackages;
    }

    public void setDtoPackages(String[] dtoPackages) {
        this.dtoPackages = new String[dtoPackages.length];
        System.arraycopy(dtoPackages, 0, this.dtoPackages, 0, this.dtoPackages.length);
    }

    public String getGenFileName() {
        return genFileName;
    }

    public void setGenFileName(String genFileName) {
        this.genFileName = genFileName;
    }

    public String getImpl() {
        return impl;
    }

    public void setImpl(String impl) {
        this.impl = impl;
    }

    public String getPackageBase() {
        return packageBase;
    }

    public void setPackageBase(String packageBase) {
        this.packageBase = packageBase;
    }

    public static void main(String[] args) {
        DtoGenerator generator = new DtoGenerator();
        for (String arg : args) {
            if (arg.startsWith("--dto_packages=")) {
                generator.setDtoPackages(arg.substring("--dto_packages=".length()));
            } else if (arg.startsWith("--gen_file_name=")) {
                generator.setGenFileName(arg.substring("--gen_file_name=".length()));
            } else if (arg.startsWith("--impl=")) {
                generator.setImpl(arg.substring("--impl=".length()));
            } else if (arg.startsWith("--package_base=")) {
                generator.setPackageBase(arg.substring("--package_base=".length()));
            } else {
                System.err.println("Unknown flag: " + arg);
                System.exit(1);
            }
        }
        generator.generate();
    }

    private void setDtoPackages(String packagesParam) {
        setDtoPackages(packagesParam.split(","));
    }

    public void generate() {

        Set<URL> urls = getClasspathForPackages(dtoPackages);
        String outputFilePath = genFileName;

        // Extract the name of the output file that will contain all the DTOs and its package.
        String myPackageBase = packageBase;
        if (!myPackageBase.endsWith("/")) {
            myPackageBase += "/";
        }
        int packageStart = outputFilePath.lastIndexOf(myPackageBase) + myPackageBase.length();
        int packageEnd = outputFilePath.lastIndexOf('/');
        String fileName = outputFilePath.substring(packageEnd + 1);
        String className = fileName.substring(0, fileName.indexOf(".java"));
        String packageName = outputFilePath.substring(packageStart, packageEnd).replace('/', '.');

        File outFile = new File(outputFilePath);

        try {
            StringBuilder sb = new StringBuilder();
            for (String dtoPackage : dtoPackages) {
                if (sb.length() > 0) {
                    sb.append(dtoPackage);
                }
                sb.append(dtoPackage);
            }

            DtoTemplate dtoTemplate = new DtoTemplate(packageName, className, getApiHash(sb.toString()),
                    impl.equals(SERVER));
            Reflections reflection = new Reflections(
                    new ConfigurationBuilder().setUrls(urls).setScanners(new TypeAnnotationsScanner()));
            List<Class<?>> dtos = new ArrayList<>(reflection.getTypesAnnotatedWith(DTO.class));

            // We sort alphabetically to ensure deterministic order of routing types.
            Collections.sort(dtos, new ClassesComparator());

            for (Class<?> clazz : dtos) {
                dtoTemplate.addInterface(clazz);
            }

            reflection = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forClassLoader())
                    .setScanners(new TypeAnnotationsScanner()));
            List<Class<?>> dtosDependencies = new ArrayList<>(reflection.getTypesAnnotatedWith(DTO.class));
            dtosDependencies.removeAll(dtos);

            reflection = new Reflections(new ConfigurationBuilder().setUrls(ClasspathHelper.forClassLoader())
                    .setScanners(new SubTypesScanner()));

            for (Class<?> clazz : dtosDependencies) {
                for (Class impl : reflection.getSubTypesOf(clazz)) {
                    if (!(impl.isInterface()
                            || urls.contains(impl.getProtectionDomain().getCodeSource().getLocation()))) {
                        if (DtoGenerator.this.impl.equals(CLIENT)) {
                            if (isClientImpl(impl)) {
                                dtoTemplate.addImplementation(clazz, impl);
                            }
                        } else if (!isClientImpl(impl)) {
                            dtoTemplate.addImplementation(clazz, impl);
                        }
                    }
                }
            }

            // Emit the generated file.
            Files.createDirectories(outFile.toPath().getParent());
            BufferedWriter writer = new BufferedWriter(new FileWriter(outFile));
            writer.write(dtoTemplate.toString());
            writer.close();

            if (impl.equals(SERVER)) {
                // Create file in META-INF/services/
                File outFile2 = new File(
                        myPackageBase + "META-INF/services/" + DtoFactoryVisitor.class.getCanonicalName());
                Files.createDirectories(outFile2.toPath().getParent());
                BufferedWriter writer2 = new BufferedWriter(new FileWriter(outFile2));
                writer2.write(packageName + "." + className);
                writer2.close();
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private boolean isClientImpl(Class impl) {
        return impl.getName().contains(".client.");
    }

    private static String getApiHash(String packageName) throws IOException {
        byte[] fileBytes = packageName.getBytes();
        HashCode hashCode = Hashing.sha1().hashBytes(fileBytes);
        return hashCode.toString();
    }

    private static Set<URL> getClasspathForPackages(String[] packages) {
        Set<URL> urls = new HashSet<>();
        for (String pack : packages) {
            urls.addAll(ClasspathHelper.forPackage(pack));
        }
        return urls;
    }

    private static class ClassesComparator implements Comparator<Class> {
        @Override
        public int compare(Class o1, Class o2) {
            return o1.getName().compareTo(o2.getName());
        }
    }
}