Package cn.org.rapid_framework.generator

Source Code of cn.org.rapid_framework.generator.Generator$GeneratorHelper

package cn.org.rapid_framework.generator;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import cn.org.rapid_framework.generator.util.AntPathMatcher;
import cn.org.rapid_framework.generator.util.BeanHelper;
import cn.org.rapid_framework.generator.util.FileHelper;
import cn.org.rapid_framework.generator.util.FreemarkerHelper;
import cn.org.rapid_framework.generator.util.GLogger;
import cn.org.rapid_framework.generator.util.GeneratorException;
import cn.org.rapid_framework.generator.util.IOHelper;
import cn.org.rapid_framework.generator.util.StringHelper;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* 代码生成器核心引擎
*
* 主要提供以下两个方法供外部使用
* <pre>
* generateBy() 用于生成文件
* deleteBy() 用于删除生成的文件
* </pre>
*
* @author badqiu
* @email badqiu(a)gmail.com
*/
public class Generator {
  private static final String GENERATOR_INSERT_LOCATION = "generator-insert-location";
  private List templateRootDirs = new ArrayList();
  private String outRootDir;
  private boolean ignoreTemplateGenerateException = true;
  private String removeExtensions = System.getProperty("generator.removeExtensions",".ftl,.vm");
  private boolean isCopyBinaryFile = true;
 
  private String includes = System.getProperty("generator.includes"); // 需要处理的模板,使用逗号分隔符,示例值: java_src/**,java_test/**
  private String excludes = System.getProperty("generator.excludes"); // 不需要处理的模板,使用逗号分隔符,示例值: java_src/**,java_test/**
  private String sourceEncoding =  System.getProperty("generator.sourceEncoding","UTF-8");
  private String outputEncoding =  System.getProperty("generator.outputEncoding","UTF-8");
 
  public Generator() {
  }
 
  public void setTemplateRootDir(File templateRootDir) {
    setTemplateRootDirs(new File[]{templateRootDir});
  }

  public void setTemplateRootDirs(File... templateRootDirs) {
    this.templateRootDirs = Arrays.asList(templateRootDirs);
  }
 
  public void addTemplateRootDir(File f) {
    templateRootDirs.add(f);
  }
 
  public boolean isIgnoreTemplateGenerateException() {
        return ignoreTemplateGenerateException;
    }
 
  public void setIgnoreTemplateGenerateException(boolean ignoreTemplateGenerateException) {
        this.ignoreTemplateGenerateException = ignoreTemplateGenerateException;
    }

    public boolean isCopyBinaryFile() {
    return isCopyBinaryFile;
  }

  public void setCopyBinaryFile(boolean isCopyBinaryFile) {
    this.isCopyBinaryFile = isCopyBinaryFile;
  }

  public String getSourceEncoding() {
    return sourceEncoding;
  }

  public void setSourceEncoding(String sourceEncoding) {
    if(sourceEncoding == null) throw new IllegalArgumentException("sourceEncoding must be not null");
    this.sourceEncoding = sourceEncoding;
  }

  public String getOutputEncoding() {
    return outputEncoding;
  }

  public void setOutputEncoding(String outputEncoding) {
    if(outputEncoding == null) throw new IllegalArgumentException("outputEncoding must be not null");
    this.outputEncoding = outputEncoding;
  }
 
  public void setIncludes(String includes) {
    this.includes = includes;
  }
  /** 设置不处理的模板路径,可以使用ant类似的值,使用逗号分隔,示例值:  **\*.ignore */
  public void setExcludes(String excludes) {
    this.excludes = excludes;
  }

  public void setOutRootDir(String v) {
    if(v == null) throw new IllegalArgumentException("outRootDir must be not null");
    this.outRootDir = v;
  }
 
  public String getOutRootDir() {
//    if(outRootDir == null) throw new IllegalStateException("'outRootDir' property must be not null.");
    return outRootDir;
  }

  public void setRemoveExtensions(String removeExtensions) {
    this.removeExtensions = removeExtensions;
  }

    public void deleteOutRootDir() throws IOException {
        if(StringHelper.isBlank(getOutRootDir())) throw new IllegalStateException("'outRootDir' property must be not null.");
        GLogger.println("[delete dir]    "+getOutRootDir());
        FileHelper.deleteDirectory(new File(getOutRootDir()));
    }
    
    /**
     * 生成文件
     * @param templateModel 生成器模板可以引用的变量
     * @param filePathModel 文件路径可以引用的变量
     * @throws Exception
     */
  public Generator generateBy(Map templateModel,Map filePathModel) throws Exception {
    processTemplateRootDirs(templateModel, filePathModel,false);
    return this;
  }

  /**
   * 删除生成的文件
   * @param templateModel 生成器模板可以引用的变量
   * @param filePathModel 文件路径可以引用的变量
   * @return
   * @throws Exception
   */
    public Generator deleteBy(Map templateModel,Map filePathModel) throws Exception {
      processTemplateRootDirs(templateModel, filePathModel,true);
      return this;
   
 
  private void processTemplateRootDirs(Map templateModel,Map filePathModel,boolean isDelete) throws Exception {
      if(StringHelper.isBlank(getOutRootDir())) throw new IllegalStateException("'outRootDir' property must be not null.");
    if(templateRootDirs.size() == 0) throw new IllegalStateException("'templateRootDirs' cannot empty");
    GeneratorException ge = new GeneratorException("generator occer error, Generator BeanInfo:"+BeanHelper.describe(this));
    for(int i = 0; i < this.templateRootDirs.size(); i++) {
      File templateRootDir = (File)templateRootDirs.get(i);
      List<Exception> exceptions = scanTemplatesAndProcess(templateRootDir,templateModel,filePathModel,isDelete);
      ge.addAll(exceptions);
    }
    if(!ge.exceptions.isEmpty()) throw ge;
  }
 
  private List<Exception> scanTemplatesAndProcess(File templateRootDir, Map templateModel,Map filePathModel,boolean isDelete) throws Exception {
    if(templateRootDir == null) throw new IllegalStateException("'templateRootDir' must be not null");
    GLogger.println("-------------------load template from templateRootDir = '"+templateRootDir.getAbsolutePath()+"' outRootDir:"+new File(outRootDir).getAbsolutePath());
   
     List srcFiles = FileHelper.searchAllNotIgnoreFile(templateRootDir);
   
    List<Exception> exceptions = new ArrayList();
    for(int i = 0; i < srcFiles.size(); i++) {
      File srcFile = (File)srcFiles.get(i);
      try {
          if(isDelete){
              new TemplateProcessor().executeDelete(templateRootDir, templateModel,filePathModel, srcFile);
          }else {
              new TemplateProcessor().executeGenerate(templateRootDir, templateModel,filePathModel, srcFile);
          }
      }catch(Exception e) {
        if (ignoreTemplateGenerateException) {
              GLogger.warn("iggnore generate error,template is:" + srcFile+" cause:"+e);
              exceptions.add(e);
          } else {
          throw e;
          }
      }
    }
    return exceptions;
  }
 
  private class TemplateProcessor {
    private GeneratorControl gg = new GeneratorControl();
    private void executeGenerate(File templateRootDir,Map templateModel, Map filePathModel ,File srcFile) throws SQLException, IOException,TemplateException {
      String templateFile = FileHelper.getRelativePath(templateRootDir, srcFile);
      if(GeneratorHelper.isIgnoreTemplateProcess(srcFile, templateFile,includes,excludes)) {
        return;
      }
     
      if(isCopyBinaryFile && FileHelper.isBinaryFile(srcFile)) {
        String outputFilepath = proceeForOutputFilepath(filePathModel, templateFile);
        GLogger.println("[copy binary file by extention] from:"+srcFile+" => "+new File(getOutRootDir(),outputFilepath));
        IOHelper.copyAndClose(new FileInputStream(srcFile), new FileOutputStream(new File(getOutRootDir(),outputFilepath)));
        return;
      }
     
            try {
                String outputFilepath = proceeForOutputFilepath(filePathModel,templateFile);
               
                initGeneratorControlProperties(srcFile,outputFilepath);
                processTemplateForGeneratorControl(templateModel, templateFile);
               
                if(gg.isIgnoreOutput()) {
                    GLogger.println("[not generate] by gg.isIgnoreOutput()=true on template:"+templateFile);
                    return;
                }
               
                if(StringHelper.isNotBlank(gg.getOutputFile())) {
                    generateNewFileOrInsertIntoFile(templateFile,gg.getOutputFile(), templateModel);
                }
      }catch(Exception e) {
          throw new RuntimeException("generate oucur error,templateFile is:" + templateFile+" => "+ gg.getOutputFile()+" cause:"+e, e);
      }
    }

      private void executeDelete(File templateRootDir,Map templateModel, Map filePathModel ,File srcFile) throws SQLException, IOException,TemplateException {
          String templateFile = FileHelper.getRelativePath(templateRootDir, srcFile);
            if(GeneratorHelper.isIgnoreTemplateProcess(srcFile, templateFile,includes,excludes)) {
                return;
            }
            String outputFilepath = proceeForOutputFilepath(filePathModel, templateFile);
          initGeneratorControlProperties(srcFile,outputFilepath);
          gg.deleteGeneratedFile = true;
          processTemplateForGeneratorControl(templateModel, templateFile);
          GLogger.println("[delete file] file:"+new File(gg.getOutputFile()).getAbsolutePath());
          new File(gg.getOutputFile()).delete();
      }
     
    private void initGeneratorControlProperties(File srcFile,String outputFile) throws SQLException {
      gg.setSourceFile(srcFile.getAbsolutePath());
      gg.setSourceFileName(srcFile.getName());
      gg.setSourceDir(srcFile.getParent());
      gg.setOutRoot(getOutRootDir());
      gg.setOutputEncoding(outputEncoding);
      gg.setSourceEncoding(sourceEncoding);
      gg.setMergeLocation(GENERATOR_INSERT_LOCATION);
      gg.setOutputFile(outputFile);
    }
 
    private void processTemplateForGeneratorControl(Map templateModel,String templateFile) throws IOException, TemplateException {
      templateModel.put("gg", gg);
      Template template = getFreeMarkerTemplate(templateFile);
      template.process(templateModel, IOHelper.NULL_WRITER);
    }
   
    /** 处理文件路径的变量变成输出路径 */
    private String proceeForOutputFilepath(Map filePathModel,String templateFile) throws IOException {
      String outputFilePath = templateFile;
     
      //TODO 删除兼容性的@testExpression
      int testExpressionIndex = -1;
      if((testExpressionIndex = templateFile.indexOf('@')) != -1) {
        outputFilePath = templateFile.substring(0, testExpressionIndex);
        String testExpressionKey = templateFile.substring(testExpressionIndex+1);
        Object expressionValue = filePathModel.get(testExpressionKey);
        if(expressionValue == null) {
          System.err.println("[not-generate] WARN: test expression is null by key:["+testExpressionKey+"] on template:["+templateFile+"]");
            return null;
        }
        if(!"true".equals(String.valueOf(expressionValue))) {
          GLogger.println("[not-generate]\t test expression '@"+testExpressionKey+"' is false,template:"+templateFile);
            return null;
        }
      }
     
      for(String removeExtension : removeExtensions.split(",")) {
        if(outputFilePath.endsWith(removeExtension)) {
          outputFilePath = outputFilePath.substring(0,outputFilePath.length() - removeExtension.length());
          break;
        }
      }
      Configuration conf = GeneratorHelper.newFreeMarkerConfiguration(templateRootDirs, sourceEncoding,"/filepath/processor/");
      return FreemarkerHelper.processTemplateString(outputFilePath,filePathModel,conf);
    }
 
    private Template getFreeMarkerTemplate(String templateName) throws IOException {
      return GeneratorHelper.newFreeMarkerConfiguration(templateRootDirs, sourceEncoding,templateName).getTemplate(templateName);
    }
 
    private void generateNewFileOrInsertIntoFile( String templateFile,String outputFilepath, Map templateModel) throws Exception {
      Template template = getFreeMarkerTemplate(templateFile);
      template.setOutputEncoding(gg.getOutputEncoding());
     
      File absoluteOutputFilePath = FileHelper.parentMkdir(outputFilepath);
      if(absoluteOutputFilePath.exists()) {
        StringWriter newFileContentCollector = new StringWriter();
        if(GeneratorHelper.isFoundInsertLocation(gg,template, templateModel, absoluteOutputFilePath, newFileContentCollector)) {
          GLogger.println("[insert]\t generate content into:"+outputFilepath);
          IOHelper.saveFile(absoluteOutputFilePath, newFileContentCollector.toString(),gg.getOutputEncoding());
          return;
        }
      }
     
      if(absoluteOutputFilePath.exists() && !gg.isOverride()) {
        GLogger.println("[not generate]\t by gg.isOverride()=false and outputFile exist:"+outputFilepath);
        return;
      }
     
      GLogger.println("[generate]\t template:"+templateFile+" ==> "+outputFilepath);
      FreemarkerHelper.processTemplate(template, templateModel, absoluteOutputFilePath,gg.getOutputEncoding());
    }
  }

  static class GeneratorHelper {
    public static boolean isIgnoreTemplateProcess(File srcFile,String templateFile,String includes,String excludes) {
      if(srcFile.isDirectory() || srcFile.isHidden())
        return true;
      if(templateFile.trim().equals(""))
        return true;
      if(srcFile.getName().toLowerCase().endsWith(".include")){
        GLogger.println("[skip]\t\t endsWith '.include' template:"+templateFile);
        return true;
      }
      templateFile = templateFile.replace('\\', '/');
      for(String exclude : StringHelper.tokenizeToStringArray(excludes,",")) {
        if(new AntPathMatcher().match(exclude.replace('\\', '/'), templateFile)) return true;
      }
      if(includes == null) return false;
      for(String include : StringHelper.tokenizeToStringArray(includes,",")) {
        if(new AntPathMatcher().match(include.replace('\\', '/'), templateFile)) return false;
      }
      return true;
    }   
   
    private static boolean isFoundInsertLocation(GeneratorControl gg,Template template, Map model, File outputFile, StringWriter newFileContent) throws IOException, TemplateException {
      LineNumberReader reader = new LineNumberReader(new FileReader(outputFile));
      String line = null;
      boolean isFoundInsertLocation = false;
     
      //FIXME 持续性的重复生成会导致out of memory
      PrintWriter writer = new PrintWriter(newFileContent);
      while((line = reader.readLine()) != null) {
        writer.println(line);
        // only insert once
        if(!isFoundInsertLocation && line.indexOf(gg.getMergeLocation()) >= 0) {
          template.process(model,writer);
          writer.println();
          isFoundInsertLocation = true;
        }
      }
     
      writer.close();
      reader.close();
      return isFoundInsertLocation;
   
    public static Configuration newFreeMarkerConfiguration(List<File> templateRootDirs,String defaultEncoding,String templateName) throws IOException {
        Configuration conf = new Configuration();
     
      FileTemplateLoader[] templateLoaders = new FileTemplateLoader[templateRootDirs.size()];
      for(int i = 0; i < templateRootDirs.size(); i++) {
        templateLoaders[i] = new FileTemplateLoader((File)templateRootDirs.get(i));
      }
      MultiTemplateLoader multiTemplateLoader = new MultiTemplateLoader(templateLoaders);
     
      conf.setTemplateLoader(multiTemplateLoader);
      conf.setNumberFormat("###############");
      conf.setBooleanFormat("true,false");
      conf.setDefaultEncoding(defaultEncoding);
     
//      String autoIncludes = new File(new File(templateName).getParent(),"macro.include").getPath();
//      List<String> availableAutoInclude = FreemarkerHelper.getAvailableAutoInclude(conf, Arrays.asList("macro.include",autoIncludes));
//      conf.setAutoIncludes(availableAutoInclude);
//      GLogger.info("[set Freemarker.autoIncludes]"+availableAutoInclude+" for templateName:"+templateName);
     
      List<String> autoIncludes = getParentPaths(templateName,"macro.include");
      List<String> availableAutoInclude = FreemarkerHelper.getAvailableAutoInclude(conf,autoIncludes);
      conf.setAutoIncludes(availableAutoInclude);
      GLogger.trace("set Freemarker.autoIncludes:"+availableAutoInclude+" for templateName:"+templateName+" autoIncludes:"+autoIncludes);
      return conf;
    }

    public static List<String> getParentPaths(String templateName,String suffix) {
      String array[] = StringHelper.tokenizeToStringArray(templateName, "\\/");
      List<String> list = new ArrayList<String>();
      list.add(suffix);
      list.add(File.separator+suffix);
      String path = "";
      for(int i = 0; i < array.length; i++){
        path = path + File.separator + array[i];
        list.add(path + File.separator+suffix);
      }
      return list;
    }
  }

  public static class GeneratorModel implements java.io.Serializable{
    public Map filePathModel;
    public Map templateModel;
    public GeneratorModel(Map templateModel, Map filePathModel) {
      this.templateModel = templateModel;
      this.filePathModel = filePathModel;
    }
  }
}
TOP

Related Classes of cn.org.rapid_framework.generator.Generator$GeneratorHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.