0%

功能菜单自动生成

文章字数:1278,阅读全文大约需要5分钟

一个用于整合不同功能的工具,在指定的包里创建功能类,并声明分类名称、功能名称、参数名称。就能自动生成调用菜单,是小工具的通用入口。

注解

  • 创建包com.colin.tool.invokeableAnnotation
  • 使用三个注解区分不同功能
  • categoryInfo注释在类上,表示类下的所有方法属于什么分类
  • functionInfo注释在方法上,表示该方法的名称及详细信息(输入帮助时展示)
  • paramName注释在方法参数,参数名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.colin.tool.invokeableAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 用于注释类的分类名
*
* @author colin.cheng
* @date 2021-12-09 10:03
* @since 1.0.0
*/
@Retention(RetentionPolicy.RUNTIME) //运行环境可用
@Target({ElementType.TYPE})
public @interface categoryInfo {
/** 分类名称 */
String value();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.colin.tool.invokeableAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 标记该方法为可调用的功能,并且添加说明
*
* @Author colin
* @Date 2021/12/5 22:29
* @Version 1.0k
*/
@Retention(RetentionPolicy.RUNTIME) //运行环境可用
@Target({ElementType.METHOD})
public @interface functionInfo {
/** 功能名称 */
String value();
/** 简介 */
String info();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.colin.tool.invokeableAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* 标记参数名称
*
* @Author colin
* @Date 2021/12/5 22:35
* @Version 1.0
*/
@Retention(RetentionPolicy.RUNTIME) //运行环境可用
@Target({ElementType.PARAMETER})
public @interface paramName {
/** 参数名 */
String value();
}

数据模型

  • 创建包com.colin.tool.model
  • 改类存储功能菜单需要的相关信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package com.colin.tool.model;

import java.util.LinkedList;
import java.util.List;

/**
* 菜单数据内容
*
* @author colin.cheng
* @date 2021-12-06
* @since 1.0.0
*/
public class InvokeMenu {
private String functionName;
private String functionTitle;
private String functionInfo;
private String categoryName;
private Object instance;
private List<String> paramInfo = new LinkedList<>();

public InvokeMenu(String functionName, String functionTitle, String functionInfo, String categoryName, Object instance) {
this.functionName = functionName;
this.functionTitle = functionTitle;
this.functionInfo = functionInfo;
this.categoryName = categoryName;
this.instance = instance;
}

public String getCategoryName() {
return categoryName;
}

public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}

public Object getInstance() {
return instance;
}

public void setInstance(Object instance) {
this.instance = instance;
}

public String getFunctionTitle() {
return functionTitle;
}

public void setFunctionTitle(String functionTitle) {
this.functionTitle = functionTitle;
}

public String getFunctionName() {
return functionName;
}

public void setFunctionName(String functionName) {
this.functionName = functionName;
}

public String getFunctionInfo() {
return functionInfo;
}

public void setFunctionInfo(String functionInfo) {
this.functionInfo = functionInfo;
}

public List<String> getParamInfo() {
return paramInfo;
}

public void setParamInfo(String paramInfo) {
this.paramInfo.add(paramInfo);
}

@Override
public String toString() {
return "InvokeMenu{" +
"functionName='" + functionName + '\'' +
", functionTitle='" + functionTitle + '\'' +
", functionInfo='" + functionInfo + '\'' +
", paramInfo=" + paramInfo +
'}';
}
}

菜单界面展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.colin.tool;

import com.colin.tool.model.InvokeMenu;
import java.util.List;
import java.util.Scanner;

/**
* 控制台界面
*
* @author colin.cheng
* @date 2021-12-06
* @since 1.0.0
*/
public class ConsoleMenu {

public void viewMenu(List<InvokeMenu> menus) {
System.out.println("--------------------------菜单------------------------");
System.out.println("请输入序号以选择功能:");
System.out.println("\t[-1] 退出");
System.out.println("\t[0] 帮助");
String currentCategory = "";
for (int i = 0; i < menus.size(); i++) {
final InvokeMenu menu = menus.get(i);
if(!menu.getCategoryName().equals(currentCategory)) {
currentCategory = menu.getCategoryName();
System.out.println("功能分类[" + currentCategory + "]");
}
System.out.println("\t[" + (i + 1) + "] " + menu.getFunctionTitle());
}
System.out.println("--------------------------结束------------------------");
}

public void viewHelp(List<InvokeMenu> menus) {
System.out.println("--------------------------帮助------------------------");
for (int i = 0; i < menus.size(); i++) {
final InvokeMenu menu = menus.get(i);
System.out.println("[" + (i + 1) + "] " +menu.getFunctionTitle() + "[" + menu.getFunctionName() + "]");
System.out.println("\t\t" + menu.getFunctionInfo());
for (int j = 0; j < menu.getParamInfo().size(); j++) {
System.out.println("\t\t参数[" + (j + 1) + "]: " + menu.getParamInfo().get(j));
}
}
System.out.println("--------------------------结束------------------------");
pause("任意输入返回菜单...");
}

public void pause(String str) {
System.out.print(str);
Scanner scanner = new Scanner(System.in);
scanner.hasNextLine();
}

public String getInput(String str) {
Scanner scanner = new Scanner(System.in);
System.out.print(str);
return scanner.next();
}
}

主类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.colin.tool;

import com.colin.tool.invokeableAnnotation.*;
import com.colin.tool.model.InvokeMenu;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;

/**
* 工具通用调用
*
* @Author colin
* @Date 2021/12/5 22:02
* @Version 1.0
*/
public class UtilToolInvoke {

public static void main(String[] args) throws Exception {
String packageName = "com.colin.tool.function";
String packagePath = packageName.replace(".", "/");
final ClassLoader loader = UtilToolInvoke.class.getClassLoader();
final URL resource = loader.getResource(packagePath);
System.out.println(resource);
final File packageFile = new File(resource.getFile());
List<Class> funClass = new LinkedList<>();
if(packageFile.isDirectory()) {
final File[] files = packageFile.listFiles();
if (files != null) {
for (File classFile : files) {
if(classFile.getName().endsWith(".class")) {
final Class<?> aClass = loader.loadClass(packageName + "." + classFile.getName().replace(".class", ""));
funClass.add(aClass);
}
}
startWithConsole(funClass);
}
}
if(funClass.size() <= 0) {
System.out.println("---------暂无功能----------");
}
System.out.println("---------end--------");
}

// private

private static void startWithConsole(List<Class> functionClasses) throws Exception {
final ConsoleMenu consoleMenu = new ConsoleMenu();
final List<InvokeMenu> menus = getFunctionList(functionClasses);
boolean isContinue = true;
while (isContinue) {
consoleMenu.viewMenu(menus);
final String input = consoleMenu.getInput("请输入您的选择,回车确认:");
if("-1".equals(input)) {
isContinue = false;
} else if("0".equals(input)) {
consoleMenu.viewHelp(menus);
} else if(Integer.parseInt(input) - 1 < menus.size()) {
final InvokeMenu invokeMenu = menus.get(Integer.parseInt(input) - 1);
final String functionName = invokeMenu.getFunctionName();
System.out.println("正在执行功能[" + invokeMenu.getFunctionTitle() + "]");
String[] values = new String[invokeMenu.getParamInfo().size()];
Class[] types = new Class[invokeMenu.getParamInfo().size()];
for (int i = 0; i < invokeMenu.getParamInfo().size(); i++) {
values[i] = consoleMenu.getInput("请输入" + invokeMenu.getParamInfo().get(i) + ":");
types[i] = String.class;
}
final Method method = invokeMenu.getInstance().getClass().getMethod(functionName, types);
String res = method.invoke(invokeMenu.getInstance(), values) + "";
consoleMenu.pause(res + ", 任意输入返回...");
} else {
consoleMenu.pause("输入错误,任意输入返回...");
}
}
}

private static List<InvokeMenu> getFunctionList(List<Class> targetClasses) throws IllegalAccessException, InstantiationException {
List<InvokeMenu> menuList = new LinkedList<>();
for (Class targetClass : targetClasses) {
boolean hasAnnotation = targetClass.isAnnotationPresent(categoryInfo.class);
if(hasAnnotation) {
categoryInfo categoryAnnotation = (categoryInfo)targetClass.getAnnotation(categoryInfo.class);
Object instance = targetClass.newInstance();
for (Method method : targetClass.getMethods()) {
hasAnnotation = method.isAnnotationPresent(functionInfo.class);
if(hasAnnotation) {
functionInfo funcAnnotation = method.getAnnotation(functionInfo.class);
InvokeMenu menu = new InvokeMenu(method.getName(), funcAnnotation.value(), funcAnnotation.info(), categoryAnnotation.value(), instance);
for (Parameter parameter : method.getParameters()) {
paramName paramName = parameter.getAnnotation(paramName.class);
menu.setParamInfo(paramName.value());
}
menuList.add(menu);
}
}
}
}
return menuList;
}
}

声明功能

上面的代码已经是完整的了,但是还没添加功能

  • 创建com.colin.tool.function包,这个包下的类会自动扫描并加载
  • 声明有哪些功能,及功能简介
  1. 之前写的加密相关功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.colin.tool.function;

import com.colin.tool.ConsoleMenu;
import com.colin.tool.invokeableAnnotation.*;
import com.colin.tool.file.EncryptionDirectoryProcessor;

/**
* 文件加密相关功能入口
*
* @author colin.cheng
* @date 2021-12-06 15:23
* @since 1.0.0
*/
@categoryInfo("文件加密")
public class CryptoFunction {

private EncryptionDirectoryProcessor processor;

@functionInfo(value = "全局密码设置", info = "加密相关功能需要预先设置该密码然后才能正常使用")
public String setGloblePwd(@paramName("密码") String pwd) {
try {
processor = new EncryptionDirectoryProcessor(pwd).defaultLog();
} catch (Exception e) {
return "设置失败";
}
return "设置成功";
}

@functionInfo(value = "开启加密功能日志打印", info = "打印扫描文件及处理进度,默认开启")
public String enableCryptoLog() {
processor.defaultLog();
return "开启成功";
}

@functionInfo(value = "关闭加密功能日志打印", info = "关闭后将静默处理")
public String disableCryptoLog() {
processor.setSearchLog(null);
processor.setProcessorLog(null);
return "已关闭";
}

@functionInfo(value = "加密文件夹", info = "将指定文件夹内文件加密")
public String encryptDirectory(@paramName(value = "文件夹路径")String path) {
try {
final ConsoleMenu consoleMenu = new ConsoleMenu();
path = path.trim();
final String input = consoleMenu.getInput("确认加密路径[" + path + "] 么? 输入y 确认,其它取消:");
if("y".equals(input)) {
processor.encryptDirectory(path);
}
} catch (Exception e) {
return "加密失败,请检查是否设置全局密码";
}
return "加密成功";
}

@functionInfo(value = "解密文件夹", info = "将指定文件夹内文件解密")
public String decryptDirectory(@paramName(value = "文件夹路径")String path) {
try {
final ConsoleMenu consoleMenu = new ConsoleMenu();
path = path.trim();
final String input = consoleMenu.getInput("确认解密路径[" + path + "] 么? 输入y 确认,其它取消:");
if("y".equals(input)) {
processor.decryptDirectory(path);
}
} catch (Exception e) {
return "解密失败,请检查是否设置全局密码";
}
return "解密成功";
}

@functionInfo(value = "查看加密文件列表", info = "查看指定的加密文件夹内文件列表")
public String listEncryptDirectory(@paramName(value = "文件夹路径")String path) {
try {
path = path.trim();
processor.listDirectory(path, true).forEach(System.out::println);
} catch (Exception e) {
return "解密失败,请检查是否设置全局密码";
}
return "搜索成功";
}
}
  1. 文件搜索相关功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.colin.tool.function;

import com.colin.tool.file.FileSearchStream;
import com.colin.tool.invokeableAnnotation.*;

import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;

/**
* 文件搜索入口
*
* @author colin.cheng
* @date 2021-12-09 10:37
* @since 1.0.0
*/
@categoryInfo("文件搜索")
public class FileSearch {

@functionInfo(value = "搜索整个文件夹", info = "列出文件夹内所有文件")
public String searchFileByPath(@paramName("需要搜索的路径") String path) {
AtomicInteger count = new AtomicInteger();
new FileSearchStream(new File(path)).forEach(file -> {
System.out.println(file.getPath());
count.getAndIncrement();
});
return "搜索结束, 一共[" + count + "]个文件";
}

@functionInfo(value = "按文件名模糊搜索", info = "正则匹配路径内所有文件")
public String searchFileLike(@paramName("搜索路径") String path, @paramName("文件名搜索正则规则") String regex) {
AtomicInteger count = new AtomicInteger();
Pattern p = Pattern.compile(regex);
new FileSearchStream(new File(path)).forEach(file -> {
if(p.matcher(file.getName()).matches()) {
System.out.println(file.getPath());
count.getAndIncrement();
}
});
return "搜索结束, 一共[" + count + "]个文件";
}

@functionInfo(value = "按文件后缀搜索", info = "所搜指定后缀(结尾)的文件")
public String searchFileBySuffix(@paramName("搜索路径") String path, @paramName("后缀") String suffix) {
AtomicInteger count = new AtomicInteger();
new FileSearchStream(new File(path)).forEach(file -> {
if(file.getName().endsWith(suffix)) {
System.out.println(file.getPath());
count.getAndIncrement();
}
});
return "搜索结束, 一共[" + count + "]个文件";
}

@functionInfo(value = "搜索指定类型的文件(img/document/application/audio/video)", info = "img[psd,pdd,gif,jpeg,jpg,png] document[doc,docx,xls,xlsx,csv,ppt,pptx,txt] application[exe,bat,cmd,sh,ink,py,class,java] audio[mp3,flac,ape,cd,wave,aiff] video[avi,mov,qt,asf,rm,navi,divX,mpeg,mpg,ogg,mod,rmvb,flv,mp4,3gp]")
public String searchFileByType(@paramName("搜索路径") String path, @paramName("文件类型") String type) {
FileSearchStream.FileSuffix suffix = null;
switch (type.toLowerCase()) {
case "img": suffix = FileSearchStream.FileSuffix.IMG; break;
case "document": suffix = FileSearchStream.FileSuffix.DOCUMENT; break;
case "application": suffix = FileSearchStream.FileSuffix.APPLICATION; break;
case "audio": suffix = FileSearchStream.FileSuffix.AUDIO; break;
case "video": suffix = FileSearchStream.FileSuffix.VIDEO; break;
default:
System.out.println("文件类型[" + type + "]未定义");
}
AtomicInteger count = new AtomicInteger();
final FileSearchStream.FileSuffix checkSuffix = suffix;
if(suffix != null) {
new FileSearchStream(new File(path)).forEach(file -> {
if(checkSuffix.check(file.getName())) {
System.out.println(file.getPath());
count.getAndIncrement();
}
});
}
return "搜索结束, 一共[" + count + "]个文件";
}
}

使用

运行UtilToolInvoke.classmain方法即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
--------------------------菜单------------------------
请输入序号以选择功能:
[-1] 退出
[0] 帮助
功能分类[文件加密]
[1] 查看加密文件列表
[2] 加密文件夹
[3] 解密文件夹
[4] 全局密码设置
[5] 开启加密功能日志打印
[6] 关闭加密功能日志打印
功能分类[文件搜索]
[7] 按文件后缀搜索
[8] 按文件名模糊搜索
[9] 搜索整个文件夹
[10] 搜索指定类型的文件(img/document/application/audio/video)
--------------------------结束------------------------
请输入您的选择,回车确认: