手记

玩转字符串篇--Gradle+代码生成器=懒人必备

前言:我是爱偷懒的勤快人

我们的口号是:
能用脑子解决的绝对不靠体力,能用电脑完成的绝对不靠脑子,能懒就懒,懒出奇迹

1.1:关于代码

你眼中的代码是什么,类的逻辑关系?方法的实现?不要忽略一个重要的本质:
代码是人可以读懂的可编译成可执行文件的字符串,所以字符串很重要。
陆陆续续写了好几篇关于字符串的文章了,量变的基类往往带来质变的升华
今天我灵光一闪,便有此文。为了不让优秀石沉大海,之前写的几篇连接放在下面:

1.玩转字符串篇--代码自动生成,解放双手
2.玩转字符串篇--数据遍地是,看你取不取
3.玩转字符串篇--练习MySQL没素材?来一波字符串操作 link
4.玩转字符串篇--替换的鬼斧神工


1.2:本篇起由

今天写Flutter自定义组件,感觉写个StatefulWidget要罗里吧嗦一大堆
而且一开始都是差不多的,于是想来个一键生成,并放到gradle里
写着写着发现用上次的字符串替代类可以用,不用走平时的拼接了
果然有心栽花花不开,无心插柳柳成荫!本文主要的是对字符串的想法


2.模板替换解析器:TemplateParser

这是我再玩转字符串篇--替换的鬼斧神工的基础上进行完善的产物

1).可指定匹配符
2).优化了结构,使用Properties替换了HashMap,并使用配置文件  
3).支持单文件和文件夹多文件替换

2.1:Properties的使用

在此之前,先说一下Properties的使用,感觉这个也挺好的,可以根据配置文件读成映射表
他继承自HashTable,只存放String到String的映射,下面来看一下他的用法
这里在项目最外面创建一个config.properties,写着键值对className=TolyWidget



public class Generation {
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        //读取属性文件a.properties
        InputStream in = new BufferedInputStream(new FileInputStream("config.properties"));
        prop.load(in);     ///加载属性列表
        Iterator<String> it = prop.stringPropertyNames().iterator();
        while (it.hasNext()) {
            String key = it.next();
            System.out.println(key + ":" + prop.getProperty(key));
        }
        in.close();
    }
}
---->[控制台输出]----
className:TolyWidget

这样就可以根据配置文件在代码中使用字符串的键值对了


2.2:解析类

最终的效果是可以通过配置文件的映射字符串,替换掉一个模板中的所有相应被标识部分
默认配置文件的位置在项目根部,名称config.properties,输出到模板的父目录同级的dest下



package generation;

import java.io.*;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TemplateParser {
    /**
     * 默认:$[X],自定义注意\\转义 
     */
    private String symbol;

    private Properties properties;

    public TemplateParser() {
        this("\\$\\[X\\]", "config.properties");
    }

    public TemplateParser(String symbol, String propFilePath) {
        this.symbol = symbol;
        loadProperties(propFilePath);
    }

    /**
     * 载入配置文件
     * @param path 配置文件路径
     */
    private void loadProperties(String path) {
        properties = new Properties();
        //读取属性文件a.properties
        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(path));
            properties.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 解析字符串
     *
     * @param target 目标字符串
     * @return 处理后的字符串
     */
    public String parser(String target) {
        String[] symbols = symbol.split("X");

        Map<Integer, String> cutPos = new TreeMap<>();
        String regex = symbols[0] + "(?<result>.*?)" + symbols[1];
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(target);
        while (matcher.find()) {
            String matchStr = matcher.group("result");
            cutPos.put(matcher.end(), matchStr);
        }

        Iterator<Map.Entry<Integer, String>> iterator = cutPos.entrySet().iterator();
        String temp;
        int offset = 0;
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> e = iterator.next();
            int k = e.getKey();
            String v = e.getValue();
            String src = "$[" + v + "]";
            String result = properties.getProperty(v);
            String substring = target.substring(0, k + offset);
            temp = substring.replace(src, result);
            target = target.replace(substring, temp);
            offset += result.length() - src.length();
        }
        return target;
    }


    /**
     * 根据路径解析所有文件
     * @param path 路径
     */
    public void parserDir(String path) {
        copyDir(path, path + "-dest");//先拷贝一分
        parserDir(new File(path + "-dest"));
    }

    private void parserDir(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files == null) {
                return;
            }
            for (File f : files) {
                if (f.isDirectory()) {
                    parserDir(f);
                } else {
                    saveFile(f, parserFile(file));
                }
            }
        } else {
            parserFile(file);
        }
    }


    /**
     * 解析一个文件
     *
     * @param path 路径
     * @return 解析后的字符串
     */
    public String parserFile(String path) {
        File file = new File(path);
        String result = parserFile(new File(path));
        String out = file.getParentFile().getParentFile().getAbsolutePath() + File.separator + "dest" + File.separator + file.getName();
        saveFile(new File(out), result);
        return result;
    }

    /**
     * 根据文件解析
     *
     * @param file 文件
     * @return 解析后的字符串
     */
    private String parserFile(File file) {
        InputStream is = null;
        StringBuilder sb = new StringBuilder();
        try {
            is = new FileInputStream(file);
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = is.read(buffer)) != -1) {
                sb.append(new String(buffer, 0, len));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return parser(sb.toString());
    }

    //==========================文件相关操作===========================
    /**
     * 保存字符串文件
     *
     * @param file    文件
     * @param content 字符串内容
     */
    private void saveFile(File file, String content) {
        ifNotExistMakeIt(file);
        FileWriter fw = null;

        try {
            fw = new FileWriter(file);//写出到磁盘
            fw.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 判断文件是否存在,不存在则创建它
     *
     * @param file
     */
    private static void ifNotExistMakeIt(File file) {
        if (file.exists()) {//2.判断文件是否存在
            return;
        }
        File parent = file.getParentFile();//3.获取父文件
        if (!parent.exists()) {
            if (!parent.mkdirs()) {//4.创建父文件
                return;
            }
        }
        try {
            file.createNewFile();//5.创建文件
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void ifNotExistMakeIt(String path) {
        File file = new File(path);//1.创建文件
        ifNotExistMakeIt(file);
    }


    /**
     * 复制整个文件夹内容
     *
     * @param oldPath String 原文件路径
     * @param newPath String 复制后路径
     * @return boolean
     */
    private void copyDir(String oldPath, String newPath) {
        try {
            (new File(newPath)).mkdirs(); //如果文件夹不存在 则建立新文件夹
            File a = new File(oldPath);
            String[] file = a.list();
            File temp = null;
            if (file == null) {
                return;
            }
            for (int i = 0; i < file.length; i++) {
                if (oldPath.endsWith(File.separator)) {
                    temp = new File(oldPath + file[i]);
                } else {
                    temp = new File(oldPath + File.separator + file[i]);
                }
                if (temp.isFile()) {
                    FileInputStream input = new FileInputStream(temp);
                    FileOutputStream output = new FileOutputStream(newPath + "/" +
                            (temp.getName()).toString());
                    byte[] b = new byte[1024 * 5];
                    int len;
                    while ((len = input.read(b)) != -1) {
                        output.write(b, 0, len);
                    }
                    output.flush();
                    output.close();
                    input.close();
                }
                if (temp.isDirectory()) {//如果是子文件夹
                    copyDir(oldPath + "/" + file[i], newPath + "/" + file[i]);
                }
            }
        } catch (Exception e) {
            System.out.println("复制出错");
            e.printStackTrace();
        }
    }
}

里面有很多文件常用的操作,也可以抽出一个工具类收藏一下


3.Gradle里如何使用Java代码

关于Gradle的知识我有一篇专文:杂篇:一代版本一代神[-Gradle-]

3.1:Gradle里的task和路径获取

新建一个task在左边Gradle->other会有相应的任务,点一下就可以运行其中的代码
可以用System.getProperty("user.dir")获取当前项目的根目录



---->[app/build.gradle最后面]----
task generationTask() {//自定义一个任务
    doFirst {
        String root=System.getProperty("user.dir");
        println("hello gradel:"+root)
}

3.2:Gradle中读取配置文件

由于Gradle中使用的是和Java兼容的Groovy语言,所以Java代码也是能运行的
这里在项目根文件下创建generation文件夹用来盛放配置文件以及模板和输出文件

task generationTask() {//自定义一个任务
    doFirst {

        String root=System.getProperty("user.dir");
        println("hello gradel:"+root)

        Properties prop = new Properties();
        //读取属性文件a.properties
        InputStream is = new BufferedInputStream(new FileInputStream(root+"/generation/config.properties"));
        prop.load(is);     ///加载属性列表
        Iterator<String> itor = prop.stringPropertyNames().iterator();
        while (itor.hasNext()) {
            String key = itor.next();
            System.out.println(key + ":" + prop.getProperty(key));
        }
        is.close();
    }
}

然后运行就可以看到获取的结果:className:TolyWidget


3.3:使用

把刚才写的类全部拷到下面。说几个比较坑的点吧

1.Groovy中正则匹配不能用分组,我勒个去  
2.符号$要用单引号,否则报错  
3.函数不能重载,我勒个去




3.4:插件以及拆分文件导入

那么大一段写在一块不怎么雅观,拆一下呗,将插件逻辑全部抽到另一个文件了
也放在generation包里,这样整个流程所需要的东西都在一起,整个gradle只管用就行了
我们只需要在意模板和配置,两个都写好之后,轻轻一点,模板中需要替换的全部搞定



---->[使用方法,app/build.gradle]----
apply from: "./generation/generation.gradle"

把这个拷过去用就行了,以后有什么写着比较烦而且没有技术含量的批量换一下呗。

// Create By 张风捷特烈(toly) in 2019.7.17 ---------

apply plugin: TemplateParserPlugin//声明使用插件

//----------------------------以下是插件部分--------------------------------
class TemplateParserPlugin implements Plugin<Project> {
   //该接口定义了一个apply()方法,在该方法中,我们可以操作Project,
   //比如向其中加入Task,定义额外的Property等。
   void apply(Project project) {
       //加载Extension
       project.extensions.create("Config", Extension)

       //使用Extension配置信息
       project.task('TemplateParser') << {
           String path = project.Config.root
           new TemplateParser(path+"config.properties")
                   .parserFile(path+"template/StatefulTemplate.txt")
       }

   }
}

class Extension {//拓展参数
   String root = System.getProperty("user.dir")+"/generation/"
}

///----------------- 以下是解析类 -----------------

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TemplateParser {
   /**
    * 默认:$[X],自定义注意\\转义
    */
   private String symbol;

   private Properties properties;

   public TemplateParser() {
       this('\\$\\[X\\]', "config.properties");
   }

   public TemplateParser( String propFilePath) {
       this('\\$\\[X\\]',propFilePath);
   }

   public TemplateParser(String symbol, String propFilePath) {
       this.symbol = symbol;
       loadProperties(propFilePath);
   }

   /**
    * 载入配置文件
    * @param path 配置文件路径
    */
   private void loadProperties(String path) {
       properties = new Properties();
       //读取属性文件a.properties
       InputStream is = null;
       try {
           is = new BufferedInputStream(new FileInputStream(path));
           properties.load(is);
       } catch (IOException e) {
           e.printStackTrace();
       } finally {
           try {
               if (is != null) {
                   is.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

   /**
    * 解析字符串
    *
    * @param target 目标字符串
    * @return 处理后的字符串
    */
   public String parser(String target) {
       String[] symbols = symbol.split("X");

       Map<Integer, String> cutPos = new TreeMap<>();
       String regex = symbols[0] + ".*?" + symbols[1];
       Pattern pattern = Pattern.compile(regex);
       Matcher matcher = pattern.matcher(target);

       while (matcher.find()) {
           String matchStr = matcher.group(0);
           String v = matchStr.split(symbols[0])[1].split(symbols[1])[0]
           cutPos.put(matcher.end(), v);
       }

       Iterator<Map.Entry<Integer, String>> iterator = cutPos.entrySet().iterator();
       String temp;
       int offset = 0;
       while (iterator.hasNext()) {
           Map.Entry<Integer, String> e = iterator.next();
           int k = e.getKey();
           String v = e.getValue();
           String src = '$[' + v + "]";
           String result = properties.getProperty(v);
           String substring = target.substring(0, k + offset);
           temp = substring.replace(src, result);
           target = target.replace(substring, temp);
           offset += result.length() - src.length();
       }
       return target;
   }


   /**
    * 根据路径解析所有文件
    * @param path 路径
    */
   public void parserDir(String path) {
       copyDir(path, path + "-dest");//先拷贝一分
       _parserDir(new File(path + "-dest"));
   }

   private void _parserDir(File file) {
       if (file.isDirectory()) {
           File[] files = file.listFiles();
           if (files == null) {
               return;
           }
           for (File f : files) {
               if (f.isDirectory()) {
                   parserDir(f);
               } else {
                   saveFile(f, parserFile(file));
               }
           }
       } else {
           parserFile(file);
       }
   }


   /**
    * 解析一个文件
    *
    * @param path 路径
    * @return 解析后的字符串
    */
   public String parserFile(String path) {
       File file = new File(path);
       String result = _parserFile(new File(path));
       String out = file.getParentFile().getParentFile().getAbsolutePath() + File.separator + "dest" + File.separator + file.getName();
       saveFile(new File(out), result);
       return result;
   }

   /**
    * 根据文件解析
    *
    * @param file 文件
    * @return 解析后的字符串
    */
   private String _parserFile(File file) {
       InputStream is = null;
       StringBuilder sb = new StringBuilder();
       try {
           is = new FileInputStream(file);
           int len = 0;
           byte[] buffer = new byte[1024];
           while ((len = is.read(buffer)) != -1) {
               sb.append(new String(buffer, 0, len));
           }
       } catch (Exception e) {
           e.printStackTrace();
       } finally {
           try {
               if (is != null) {
                   is.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
       return parser(sb.toString());
   }

   //=============================================文件相关操作=============================================

   /**
    * 保存字符串文件
    *
    * @param file    文件
    * @param content 字符串内容
    */
   private void saveFile(File file, String content) {
       ifNotExistMakeIt(file);
       FileWriter fw = null;

       try {
           fw = new FileWriter(file);//写出到磁盘
           fw.write(content);
       } catch (IOException e) {
           e.printStackTrace();
       } finally {
           try {
               if (fw != null) {
                   fw.close();
               }
           } catch (IOException e) {
               e.printStackTrace();
           }
       }

   }

   /**
    * 判断文件是否存在,不存在则创建它
    *
    * @param file
    */
   private static void ifNotExistMakeIt(File file) {
       if (file.exists()) {//2.判断文件是否存在
           return;
       }
       File parent = file.getParentFile();//3.获取父文件
       if (!parent.exists()) {
           if (!parent.mkdirs()) {//4.创建父文件
               return;
           }
       }
       try {
           file.createNewFile();//5.创建文件
       } catch (IOException e) {
           e.printStackTrace();
       }
   }

   private static void ifNotExistMakeIt(String path) {
       File file = new File(path);//1.创建文件
       ifNotExistMakeIt(file);
   }

   /**
    * 复制整个文件夹内容
    *
    * @param oldPath String 原文件路径
    * @param newPath String 复制后路径
    * @return boolean
    */
   private void copyDir(String oldPath, String newPath) {
       try {
           (new File(newPath)).mkdirs(); //如果文件夹不存在 则建立新文件夹
           File a = new File(oldPath);
           String[] file = a.list();
           File temp = null;
           if (file == null) {
               return;
           }
           for (int i = 0; i < file.length; i++) {
               if (oldPath.endsWith(File.separator)) {
                   temp = new File(oldPath + file[i]);
               } else {
                   temp = new File(oldPath + File.separator + file[i]);
               }
               if (temp.isFile()) {
                   FileInputStream input = new FileInputStream(temp);
                   FileOutputStream output = new FileOutputStream(newPath + "/" +
                           (temp.getName()).toString());
                   byte[] b = new byte[1024 * 5];
                   int len;
                   while ((len = input.read(b)) != -1) {
                       output.write(b, 0, len);
                   }
                   output.flush();
                   output.close();
                   input.close();
               }
               if (temp.isDirectory()) {//如果是子文件夹
                   copyDir(oldPath + "/" + file[i], newPath + "/" + file[i]);
               }
           }
       } catch (Exception e) {
           System.out.println("复制整个文件夹内容操作出错");
           e.printStackTrace();
       }
   }
}


 


0人推荐
随时随地看视频
慕课网APP