// 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 org.eclipse.che.dto.generator; import org.eclipse.che.dto.server.DtoFactoryVisitor; import org.eclipse.che.dto.shared.DTO; import org.eclipse.che.dto.shared.DTOImpl; 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.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 { /** 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); genFileName = genFileName.replace('/', File.separatorChar); 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 += "/"; } myPackageBase = myPackageBase.replace('/', File.separatorChar); int packageStart = outputFilePath.lastIndexOf(myPackageBase) + myPackageBase.length(); int packageEnd = outputFilePath.lastIndexOf(File.separatorChar); String fileName = outputFilePath.substring(packageEnd + 1); String className = fileName.substring(0, fileName.indexOf(".java")); String packageName = outputFilePath.substring(packageStart, packageEnd).replace(File.separatorChar, '.'); 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, impl); Reflections reflection = new Reflections(new ConfigurationBuilder().setUrls(urls).setScanners(new SubTypesScanner(), 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) { // DTO are interface if (clazz.isInterface()) { dtoTemplate.addInterface(clazz); } } reflection = new Reflections( new ConfigurationBuilder().setUrls(ClasspathHelper.forClassLoader()).setScanners(new SubTypesScanner(), 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 ("client".equals(dtoTemplate.getImplType())) { if (isClientImpl(impl)) { dtoTemplate.addImplementation(clazz, impl); } } else if ("server".equals(dtoTemplate.getImplType())) { if (isServerImpl(impl)) { dtoTemplate.addImplementation(clazz, impl); } } } } } // Emit the generated file. Files.createDirectories(outFile.toPath().getParent()); try (BufferedWriter writer = new BufferedWriter(new FileWriter(outFile))) { writer.write(dtoTemplate.toString()); } if ("server".equals(impl)) { // Create file in META-INF/services/ File outServiceFile = new File(myPackageBase + "META-INF/services/" + DtoFactoryVisitor.class.getCanonicalName()); Files.createDirectories(outServiceFile.toPath().getParent()); try (BufferedWriter serviceFileWriter = new BufferedWriter(new FileWriter(outServiceFile))) { serviceFileWriter.write(packageName + "." + className); } } } catch (IOException e) { e.printStackTrace(); } } private boolean isClientImpl(Class<?> impl) { DTOImpl a = impl.getAnnotation(DTOImpl.class); return a != null && "client".equals(a.value()); } private boolean isServerImpl(Class<?> impl) { DTOImpl a = impl.getAnnotation(DTOImpl.class); return a != null && "server".equals(a.value()); } 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()); } } }