小鸭子的学习笔记duck

Duck Blog

唐如飞

( ^∀^)/欢迎\( ^∀^)

79 文章数
14 评论数

IDEA插件开发常用工具类

tangrufei
2023-06-02 / 0 评论 / 149 阅读 / 0 点赞

获取文件后缀名

public class FileExtensionUtil {
    public static String getFileExtension(String fileName) {
        if (fileName != null && fileName.lastIndexOf(".") != -1) {
            return fileName.substring(fileName.lastIndexOf(".") + 1);
        }
        return "";
    }
}

获取JAVA文件实际的类型

判断JAVA文件类型是CLASS还是INTERFACE或者ENUM或者ABSTRACT_CLASS

依赖引入

这是gradle的方式引入,Maven同理

implementation 'com.github.javaparser:javaparser-core:3.25.3'

定义常量

public enum FileType {
    CLASS,
    INTERFACE,
    ABSTRACT_CLASS,
    ENUM,
    UNKNOWN
}

工具类编码

public class JavaFileTypeChecker {
    public static FileType getFileType(String filePath) {
        try {
            CompilationUnit cu = StaticJavaParser.parse(new File(filePath));

            if (cu.getType(0) instanceof ClassOrInterfaceDeclaration) {
                ClassOrInterfaceDeclaration classOrInterfaceDeclaration =
                        (ClassOrInterfaceDeclaration) cu.getType(0);

                if (classOrInterfaceDeclaration.isInterface()) {
                    return FileType.INTERFACE;
                } else if (classOrInterfaceDeclaration.isAbstract()) {
                    return FileType.ABSTRACT_CLASS;
                } else {
                    return FileType.CLASS;
                }
            } else if (cu.getType(0) instanceof EnumDeclaration) {
                return FileType.ENUM;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return FileType.UNKNOWN;
    }
}

集合工具类

public class CollectionUtil {

    /**
     * 判断集合是否为空的
     *
     * @param collection 集合对象
     * @return 是否为空
     */
    public static boolean isEmpty(Collection<?> collection) {
        return collection == null || collection.isEmpty();
    }

    /**
     * 判断map是否为空的
     *
     * @param map map对象
     * @return 是否为空
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return map == null || map.isEmpty();
    }

代码对比工具类

public class CompareFileUtils {

    /**
     * 显示文件对比框
     *
     * @param project   项目
     * @param leftFile  左边的文件
     * @param rightFile 右边的文件
     */
    public static void showCompareWindow(Project project, VirtualFile leftFile, VirtualFile rightFile) {

        try {
            Class<?> cls = Class.forName("com.intellij.diff.actions.impl.MutableDiffRequestChain");
            // 新版支持
            DiffContentFactory contentFactory = DiffContentFactory.getInstance();
            DiffRequestFactory requestFactory = DiffRequestFactory.getInstance();

            DiffContent leftContent = contentFactory.create(project, leftFile);
            DiffContent rightContent = contentFactory.create(project, rightFile);

            DiffRequestChain chain = (DiffRequestChain) cls.getConstructor(DiffContent.class, DiffContent.class).newInstance(leftContent, rightContent);
//            MutableDiffRequestChain chain = new MutableDiffRequestChain(leftContent, rightContent);

            cls.getMethod("setWindowTitle", String.class).invoke(chain, requestFactory.getTitle(leftFile, rightFile));
            cls.getMethod("setTitle1", String.class).invoke(chain, requestFactory.getContentTitle(leftFile));
            cls.getMethod("setTitle2", String.class).invoke(chain, requestFactory.getContentTitle(rightFile));
//            chain.setWindowTitle(requestFactory.getTitle(leftFile, rightFile));
//            chain.setTitle1(requestFactory.getContentTitle(leftFile));
//            chain.setTitle2(requestFactory.getContentTitle(rightFile));
            DiffManager.getInstance().showDiff(project, chain, DiffDialogHints.MODAL);
        } catch (ClassNotFoundException e) {
            // 旧版兼容
            DiffRequest diffRequest = DiffRequestFactory.getInstance().createFromFiles(project, leftFile, rightFile);
            DiffManager.getInstance().showDiff(project, diffRequest, DiffDialogHints.MODAL);
        } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) {
            ExceptionUtil.rethrow(e);
        }
    }

}

文档注释工具类

public class DocCommentUtils {

    /**
     * 获取注释信息,获取第一条文本类型注释内容,不存在则返回null
     *
     * @param docComment 文档注释
     * @return 注释内容
     */
    public static String getComment(@Nullable PsiDocComment docComment) {
        if (docComment == null) {
            return null;
        }
        return Arrays.stream(docComment.getDescriptionElements())
                .filter(o -> o instanceof PsiDocToken)
                .map(PsiElement::getText)
                .findFirst()
                .map(String::trim)
                .orElse(null);
    }

}

json工具类

public class JSON {

    private static final ObjectMapper INSTANCE;

    private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

    static {
        INSTANCE = new ObjectMapper();
        // 禁止将日期序列化成时间戳
        INSTANCE.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 禁止属性不存在时报错
        INSTANCE.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        // 允许字符串转换成数组
        INSTANCE.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);
        // 读取到未知的枚举值转换成空
        INSTANCE.enable(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL);
        // 禁用科学计数法
        INSTANCE.enable(DeserializationFeature.USE_BIG_INTEGER_FOR_INTS);
        INSTANCE.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
        // 序列化忽略空值
        INSTANCE.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 配置时间格式
        INSTANCE.setDateFormat(new SimpleDateFormat(DATE_FORMAT));
    }

    public static ObjectMapper getInstance() {
        return INSTANCE;
    }

    /**
     * 将json字符串转换成java对象
     *
     * @param json json字符串
     * @param cls  java对象类型
     * @param <T>  对象类型
     * @return 对象
     */
    public static <T> T parse(String json, Class<T> cls) {
        try {
            return INSTANCE.readValue(json, cls);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 将json字符串转换成java对象
     *
     * @param json json字符串
     * @param type java对象类型
     * @param <T>  对象类型
     * @return 对象
     */
    public static <T> T parse(String json, TypeReference<T> type) {
        try {
            return INSTANCE.readValue(json, type);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 将json对象转换成json字符串
     *
     * @param obj 对象
     * @return json字符串
     */
    public static String toJson(Object obj) {
        try {
            return INSTANCE.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 将json对象转换成json字符串
     *
     * @param obj 对象
     * @return json字符串
     */
    public static String toJsonByFormat(Object obj) {
        try {
            return INSTANCE.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException(e);
        }
    }

    public static JsonNode readTree(Object obj) {
        try {
            if (obj instanceof String) {
                return INSTANCE.readTree((String) obj);
            } else if (obj instanceof byte[]) {
                return INSTANCE.readTree((byte[]) obj);
            } else if (obj instanceof InputStream) {
                return INSTANCE.readTree((InputStream) obj);
            } else if (obj instanceof URL) {
                return INSTANCE.readTree((URL) obj);
            } else if (obj instanceof File) {
                return INSTANCE.readTree((File) obj);
            } else {
                // 其他对象,转字符串再转JsonNode
                return INSTANCE.readTree(toJson(obj));
            }
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
    }
}

消息弹框工具类

/**
 * 消息弹框工具类
 * 对message dialog弹框进行兼容处理
 */
public class MessageDialogUtils {

    /**
     * yes no确认框
     *
     * @param msg 消息
     * @return 是否确认
     */
    public static boolean yesNo(String msg) {
        return yesNo(null, msg);
    }

    /**
     * yes no确认框
     *
     * @param project 项目对象
     * @param msg     消息
     * @return 是否确认
     */
    public static boolean yesNo(Project project, String msg) {
        Object[] options = new Object[]{"Yes", "No"};
        return JOptionPane.showOptionDialog(null,
                msg, GlobalDict.TITLE_INFO,
                JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
                UIUtil.getQuestionIcon(),
                options, options[0]) == 0;
    }

    /**
     * 显示确认框
     *
     * @param msg     确认框消息
     * @param project 项目
     * @return 点击按钮
     */
    public static int yesNoCancel(Project project, String msg, String yesText, String noText, String cancelText) {
        Object[] options = new Object[]{yesText, noText, cancelText};
        return JOptionPane.showOptionDialog(null,
                msg, GlobalDict.TITLE_INFO,
                JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
                UIUtil.getQuestionIcon(),
                options, options[0]);
    }

}

模块工具类

public final class ModuleUtils {
    /**
     * 禁用构造方法
     */
    private ModuleUtils() {
        throw new UnsupportedOperationException();
    }

    /**
     * 获取module路径
     *
     * @param module 模块
     * @return 路径
     */
    public static VirtualFile getModuleDir(@NotNull Module module) {
        // 优先使用ModuleRootManager来获取module路径
        ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
        for (VirtualFile contentRoot : moduleRootManager.getContentRoots()) {
            if (contentRoot.isDirectory() && contentRoot.getName().equals(module.getName())) {
                return contentRoot;
            }
        }
        String modulePath = ModuleUtil.getModuleDirPath(module);
        // 统一路径分割符号
        modulePath = modulePath.replace("\\", "/");
        // 尝试消除不正确的路径
        if (modulePath.contains(".idea/modules/")) {
            modulePath = modulePath.replace(".idea/modules/","");
        }
        if (modulePath.contains(".idea/modules")) {
            modulePath = modulePath.replace(".idea/modules","");
        }
        if (modulePath.contains("/.idea")) {
            modulePath = modulePath.replace("/.idea","");
        }
        VirtualFile dir = VirtualFileManager.getInstance().findFileByUrl(String.format("file://%s", modulePath));
        if (dir == null) {
            Messages.showInfoMessage("无法获取Module路径, path=" + modulePath, GlobalDict.TITLE_INFO);
        }
        return dir;
    }

    /**
     * 获取模块的源代码文件夹,不存在
     *
     * @param module 模块对象
     * @return 文件夹路径
     */
    public static VirtualFile getSourcePath(@NotNull Module module) {
        List<VirtualFile> virtualFileList = ModuleRootManager.getInstance(module).getSourceRoots(JavaSourceRootType.SOURCE);
        if (CollectionUtil.isEmpty(virtualFileList)) {
            VirtualFile modulePath = getModuleDir(module);
            // 尝试智能识别源代码路径(通过上面的方式,IDEA不能百分百拿到源代码路径)
            VirtualFile srcDir = VfsUtil.findRelativeFile(modulePath, "src", "main", "java");
            if (srcDir != null && srcDir.isDirectory()) {
                return srcDir;
            }
            return modulePath;
        }
        if (virtualFileList.size() > 1) {
            for (VirtualFile file : virtualFileList) {
                String tmpPath = file.getPath();
                if (!tmpPath.contains("build") && !tmpPath.contains("generated")) {
                    return file;
                }
            }
        }
        return virtualFileList.get(0);
    }

    /**
     * 判断模块是否存在源代码文件夹
     *
     * @param module 模块对象
     * @return 是否存在
     */
    public static boolean existsSourcePath(Module module) {
        return !CollectionUtil.isEmpty(ModuleRootManager.getInstance(module).getSourceRoots(JavaSourceRootType.SOURCE));
    }
}

命名工具类

public class NameUtils {
    private volatile static NameUtils nameUtils;

    /**
     * 单例模式
     */
    public static NameUtils getInstance() {
        if (nameUtils == null) {
            synchronized (NameUtils.class) {
                if (nameUtils == null) {
                    nameUtils = new NameUtils();
                }
            }
        }
        return nameUtils;
    }

    /**
     * 私有构造方法
     */
    NameUtils() {
    }

    /**
     * 转驼峰命名正则匹配规则
     */
    private static final Pattern TO_HUMP_PATTERN = Pattern.compile("[-_]([a-z0-9])");
    private static final Pattern TO_LINE_PATTERN = Pattern.compile("[A-Z]+");

    /**
     * 首字母大写方法
     *
     * @param name 名称
     * @return 结果
     */
    public String firstUpperCase(String name) {
        return StringUtils.capitalize(name);
    }

    /**
     * 首字母小写方法
     *
     * @param name 名称
     * @return 结果
     */
    public String firstLowerCase(String name) {
        return StringUtils.uncapitalize(name);
    }

    /**
     * 驼峰转下划线,全小写
     *
     * @param str 驼峰字符串
     * @return 下划线字符串
     */
    public String hump2Underline(String str) {
        if (StringUtils.isEmpty(str)) {
            return str;
        }
        Matcher matcher = TO_LINE_PATTERN.matcher(str);
        StringBuffer buffer = new StringBuffer();
        while (matcher.find()) {
            if (matcher.start() > 0) {
                matcher.appendReplacement(buffer, "_" + matcher.group(0).toLowerCase());
            } else {
                matcher.appendReplacement(buffer, matcher.group(0).toLowerCase());
            }
        }
        matcher.appendTail(buffer);
        return buffer.toString();
    }

    /**
     * 通过java全名获取类名
     *
     * @param fullName 全名
     * @return 类名
     */
    public String getClsNameByFullName(String fullName) {
        int genericIdx = fullName.indexOf('<');
        if (genericIdx == -1) {
            return fullName.substring(fullName.lastIndexOf('.') + 1);
        }
        String className = fullName.substring(0, genericIdx);
        return fullName.substring(className.lastIndexOf('.') + 1);
    }

    /**
     * 通过java全名获取类名
     *
     * @param fullName 全名
     * @return 类名
     */
    public String getClsFullNameRemoveGeneric(String fullName) {
        int genericIdx = fullName.indexOf('<');
        if (genericIdx == -1) {
            return fullName;
        }
        return fullName.substring(0, genericIdx);
    }

    /**
     * 下划线中横线命名转驼峰命名(属性名)
     *
     * @param name 名称
     * @return 结果
     */
    public String getJavaName(String name) {
        if (StringUtils.isEmpty(name)) {
            return name;
        }
        // 强转全小写
        name = name.toLowerCase();
        Matcher matcher = TO_HUMP_PATTERN.matcher(name.toLowerCase());
        StringBuffer buffer = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(buffer, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(buffer);
        return buffer.toString();
    }

    /**
     * 下划线中横线命名转驼峰命名(类名)
     *
     * @param name 名称
     * @return 结果
     */
    public String getClassName(String name) {
        return firstUpperCase(getJavaName(name));
    }

    /**
     * 任意对象合并工具类
     *
     * @param objects 任意对象
     * @return 合并后的字符串结果
     */
    public String append(Object... objects) {

        if (objects == null || objects.length == 0) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        for (Object s : objects) {
            if (s != null) {
                builder.append(s);
            }
        }
        return builder.toString();
    }
}

IDEA项目相关工具类

public class ProjectUtils {

    /**
     * 获取当前项目对象
     *
     * @return 当前项目对象
     */
    public static Project getCurrProject() {
        ProjectManager projectManager = ProjectManager.getInstance();
        Project[] openProjects = projectManager.getOpenProjects();
        if (openProjects.length == 0) {
            // 在未打开任何项目,进入到设置页面时会出现
            return projectManager.getDefaultProject();
        } else if (openProjects.length == 1) {
            // 只存在一个打开的项目则使用打开的项目
            return openProjects[0];
        }

        //如果有项目窗口处于激活状态
        try {
            WindowManager wm = WindowManager.getInstance();
            for (Project project : openProjects) {
                Window window = wm.suggestParentWindow(project);
                if (window != null && window.isActive()) {
                    return project;
                }
            }
        } catch (Exception ignored) {
        }

        //否则使用默认项目
        return projectManager.getDefaultProject();
    }

    /**
     * 进行旧版本兼容,该方法已经存在 @see {@link com.intellij.openapi.project.ProjectUtil#guessProjectDir(Project)}
     *
     * @param project 项目对象
     * @return 基本目录
     */
    public static VirtualFile getBaseDir(Project project) {
        if (project.isDefault()) {
            return null;
        }
        Module[] modules = ModuleManager.getInstance(project).getModules();
        Module module = null;
        if (modules.length == 1) {
            module = modules[0];
        } else {
            for (Module item : modules) {
                if (item.getName().equals(project.getName())) {
                    module = item;
                    break;
                }
            }
        }
        if (module != null) {
            ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(module);
            for (VirtualFile contentRoot : moduleRootManager.getContentRoots()) {
                if (contentRoot.isDirectory() && contentRoot.getName().equals(module.getName())) {
                    return contentRoot;
                }
            }
        }
        String basePath = project.getBasePath();
        if (basePath == null) {
            throw new NullPointerException();
        }
        return LocalFileSystem.getInstance().findFileByPath(basePath);
    }
}

反射工具类

/**
 * 反射工具
 */
public class ReflectionUtils {
    /**
     * 获取泛型类型的类型
     * @param obj
     * @param index
     * @return
     */
    public static Class<?> getGenericClass(Object obj, int index) {
        // 获取泛型接口
        Type type = obj.getClass().getGenericInterfaces()[0];
        if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type targetType = parameterizedType.getActualTypeArguments()[index];
            return (Class<?>) targetType;
        } else {
            // 不存在泛型
            throw new IllegalArgumentException(obj.getClass() + " not found generic");
        }
    }

    /**
     * 获取声明的方法
     * @param clazz
     * @param name
     * @param parameterTypes
     * @return
     */

    public static Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
        Class<?> searchType = clazz;
        while (searchType != null) {
            Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
            for (Method method : methods) {
                if (!Objects.equals(name, method.getName())) {
                    continue;
                }
                if (parameterTypes == null || Arrays.equals(parameterTypes, method.getParameterTypes())) {
                    return method;
                }
            }
            searchType = searchType.getSuperclass();
        }
        return null;
    }

    /**
     * 获取类的所有public方法
     * @param clazz
     * @return
     */

    private static Method[] getDeclaredMethods(Class<?> clazz) {
        Method[] result;
        try {
            Method[] declaredMethods = clazz.getDeclaredMethods();
            List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
            if (defaultMethods != null) {
                result = new Method[declaredMethods.length + defaultMethods.size()];
                System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
                int index = declaredMethods.length;
                for (Method defaultMethod : defaultMethods) {
                    result[index] = defaultMethod;
                    index++;
                }
            } else {
                result = declaredMethods;
            }
        } catch (Throwable ex) {
            throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +
                    "] from ClassLoader [" + clazz.getClassLoader() + "]", ex);
        }
        return result;
    }

    /**
     * 查找接口上的具体方法
     * @param clazz
     * @return
     */
    private static List<Method> findConcreteMethodsOnInterfaces(Class<?> clazz) {
        List<Method> result = null;
        for (Class<?> ifc : clazz.getInterfaces()) {
            for (Method ifcMethod : ifc.getMethods()) {
                if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
                    if (result == null) {
                        result = new ArrayList<>();
                    }
                    result.add(ifcMethod);
                }
            }
        }
        return result;
    }

}

字符串工具类

添加字符串工具类,为了兼容JB的各种产品,尽量不要用第三方工具包

public class StringUtils {

    /**
     * 首字母处理方法
     */
    private static final BiFunction<String, Function<Integer, Integer>, String> FIRST_CHAR_HANDLER_FUN = (str, firstCharFun) -> {
        int strLen;
        if (str == null || (strLen = str.length()) == 0) {
            return str;
        }

        final int firstCodepoint = str.codePointAt(0);
        final int newCodePoint = firstCharFun.apply(firstCodepoint);
        if (firstCodepoint == newCodePoint) {
            // already capitalized
            return str;
        }

        // cannot be longer than the char array
        final int[] newCodePoints = new int[strLen];
        int outOffset = 0;
        // copy the first codepoint
        newCodePoints[outOffset++] = newCodePoint;
        for (int inOffset = Character.charCount(firstCodepoint); inOffset < strLen; ) {
            final int codepoint = str.codePointAt(inOffset);
            // copy the remaining ones
            newCodePoints[outOffset++] = codepoint;
            inOffset += Character.charCount(codepoint);
        }
        return new String(newCodePoints, 0, outOffset);
    };

    /**
     * 判断是空字符串
     *
     * @param cs 字符串
     * @return 是否为空
     */
    public static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    /**
     * 首字母大写方法
     * @param str 字符串
     * @return 首字母大写结果
     */
    public static String capitalize(final String str) {
        return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toTitleCase);
    }

    /**
     * 首字母小写方法
     * @param str 字符串
     * @return 首字母小写结果
     */
    public static String uncapitalize(final String str) {
        return FIRST_CHAR_HANDLER_FUN.apply(str, Character::toLowerCase);
    }
}

Velocity工具类

Velocity工具类,主要用于代码生成

public class VelocityUtils {
    /**
     * velocity配置
     */
    private static final Properties INIT_PROP;

    static {
        // 设置初始化配置
        INIT_PROP = new Properties();
        // 修复部分用户的velocity日志记录无权访问velocity.log文件问题
        INIT_PROP.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.Log4JLogChute");
        INIT_PROP.setProperty("runtime.log.logsystem.log4j.logger", "velocity");
    }

    /**
     * 禁止创建实例对象
     */
    private VelocityUtils() {
        throw new UnsupportedOperationException();
    }

    /**
     * 渲染模板
     *
     * @param template 模板字符串
     * @param map      参数集合
     * @return 渲染结果
     */
    public static String generate(String template, Map<String, Object> map) {
        // 每次创建一个新实例,防止velocity缓存宏定义
        VelocityEngine velocityEngine = new VelocityEngine(INIT_PROP);
        // 创建上下文对象
        VelocityContext velocityContext = new VelocityContext();
        if (map != null) {
            map.forEach(velocityContext::put);
        }
        StringWriter stringWriter = new StringWriter();
        try {
            // 生成代码
            velocityEngine.evaluate(velocityContext, stringWriter, "Velocity Code Generate", template);
        } catch (Exception e) {
            // 将异常全部捕获,直接返回,用于写入模板
            StringBuilder builder = new StringBuilder("在生成代码时,模板发生了如下语法错误:\n");
            StringWriter writer = new StringWriter();
            e.printStackTrace(new PrintWriter(writer));
            builder.append(writer.toString());
            return builder.toString().replace("\r", "");
        }
        String code = stringWriter.toString();
        // 清除前面空格
        StringBuilder sb = new StringBuilder(code);
        while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
            sb.deleteCharAt(0);
        }
        // 返回结果
        return sb.toString();
    }
}
文章不错,扫码支持一下吧~
上一篇 下一篇
评论
来首音乐
光阴似箭
今日已经过去小时
这周已经过去
本月已经过去
今年已经过去个月