前言:我是爱偷懒的勤快人
我们的口号是:
能用脑子解决的绝对不靠体力,能用电脑完成的绝对不靠脑子,能懒就懒,懒出奇迹
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只管用就行了
我们只需要在意模板和配置,两个都写好之后,轻轻一点,模板中需要替换的全部搞定
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();
}
}
}