admin管理员组

文章数量:1582375

文章目录

  • 一,序
  • 二,逸事
  • 三,为什么今天要提这个工具
  • 四,操作界面
    • 1. 文章发表者备份项目步骤
    • 2. 文章发表者恢复项目操作步骤
    • 3. 文章阅读者恢复项目步骤
  • 五,附件
    • 1. 示例备份文件
    • 2. 神奇代码备份恢复工具源码备份

一,序

软件行业流传着一幅漫画:开发软件就像制造小轿车,不是一开始就有设计图,也不是将轮子、车身、车门、发动机按部就班安装上去就可以的,而是大概先出现独轮车,接着出现自行车,然后是滑板车,之后是三轮自行车,继而是两轮摩托车··……

如此反复迭代,最后才得到成型的小轿车。这幅漫画讽刺的是开发新系统时“想当然”的做法,反映的是真实的探索过程。

其实,不仅开发系统是这样,即使是开发一个小工具,也遵循类似的原则,毕竟大部分软件被开发出来,是需要去解决实际中遇到的难题的。

二,逸事

n年前,小C曾入职一家软件公司,公司信息安全管理比较严格,员工办公电脑无法接入任何外部未授权设备。

在工作之中,也曾经做过一些技术预研,积累了一些代码。

公司有一个技术论坛,方便大家技术交流。论坛发帖采用后台审核机制。大家可以把自己遇到的问题以及代码发出来切磋。

对于这样的机制,少量的内容是合适的。一次偶然的机会发现论坛里面的短消息功能可以发送比较长的文本内容。

于是,问题便转变成了:如何把一个项目文件的内容快速的放到一个文本文件里面,并且接受到这个文本内容后如何快速恢复成项目原来的结构。

从软件需求的角度来说,这里面引申出2个基本需求:

  1. 项目文件备份到单个的文本文件
  2. 单个的文本文件恢复成项目文件

三,为什么今天要提这个工具

因为今天,程序员其实是最具有开源精神的一类团体,有太多的内容和Idea需要通过code来交流。

虽然说有github、gitee等代码托管平台,但那是和文章是属于2套系统,而且代码托管平台上的代码是随时变动的。

于是便催生了发表文章的同时,能够方便将示例代码一同提交的需求,而且这个代码还需要反映出项目的结构,这跟上面的2个需求是不谋而合的!

四,操作界面

运行1、2下载 procode-0.0.1.jar

运行3下载 procode-simple-0.0.1.jar

1. 文章发表者备份项目步骤

首先,选择需要备份的项目目录,并选择需要备份的文件类型

点击开始备份按钮,生成bak文件,成功后给出提示

备份成功后,提示是否查看备份文件
选择ok,则打开备份文件所在目录

2. 文章发表者恢复项目操作步骤

切换到代码恢复tab,选择bak文件,以及恢复文件保存目录

点击开始恢复

恢复后的文件如图:

使用开发工具导入如下

3. 文章阅读者恢复项目步骤

参考 https://blog.csdn/qq_16127313/article/details/133792839

五,附件

1. 示例备份文件

上面步骤演示生成的备份文件 spring-config-refresh_10241110.bak,大家可以自行验证文件恢复功能!

//goto pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0"
	xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.fly</groupId>
	<artifactId>spring-config-refresh</artifactId>
	<version>1.0</version>
	<packaging>jar</packaging>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.4.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>commons-configuration</groupId>
			<artifactId>commons-configuration</artifactId>
			<version>1.10</version>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.6</version>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>${project.artifactId}-${project.version}</finalName>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>
//goto src\main\java\com\fly\demo\Application.java

package com.fly.demo;

import java.io.IOException;
import java.net.URL;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.util.ResourceUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * SpringBoot 启动入口
 * 
 * @author 00fly
 * @version [版本号, 2018年7月20日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
@EnableScheduling
@SpringBootApplication
public class Application implements CommandLineRunner
{
    public static void main(String[] args)
    {
        SpringApplication.run(Application.class, args);
    }
    
    @Override
    public void run(String... args)
        throws IOException
    {
        URL url = Application.class.getProtectionDomain().getCodeSource().getLocation();
        log.info("Location URL Path: {}", url.getPath());
        
        URL url2 = new ClassPathResource("cron").getURL();
        log.info("url2 Path: {}", url2.getPath());
        log.info("ResourceUtils.isFileURL: {}", ResourceUtils.isFileURL(url2));
        log.info("ResourceUtils.isJarURL: {}", ResourceUtils.isJarURL(url2));
        log.info("ResourceUtils.isJarFileURL: {}", ResourceUtils.isJarFileURL(url2));
    }
}
//goto src\main\java\com\fly\demo\auto\job\ShowJob.java
package com.fly.demo.auto.job;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;

import com.fly.demo.auto.RedisConfig;
import com.fly.demo.auto.ReloadConfig;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Configuration
public class ShowJob
{
    @Autowired
    RedisConfig redisConfig;
    
    @Autowired
    ReloadConfig reloadConfig;
    
    /**
     * 默认线程池的大小为1,配置线程池支持多个定时任务线程并发执行
     * 
     * @return
     * @see [类、类#方法、类#成员]
     */
    @Bean
    ScheduledExecutorService scheduledExecutorService()
    {
        // return Executors.newScheduledThreadPool(8);
        return new ScheduledThreadPoolExecutor(8, new CustomizableThreadFactory("schedule-pool-"));
    }
    
    @Scheduled(cron = "0/5 * 7-23 * * ?")
    public void run()
    {
        reloadConfig.refresh(redisConfig);
        log.info("redisConfig por={}", redisConfig.getPort());
    }
}
//goto src\main\java\com\fly\demo\auto\RedisConfig.java
package com.fly.demo.auto;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

/**
 * 
 * 配置文件值注入
 * 
 * @author 00fly
 * @version [版本号, 2018年7月20日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Configuration
@PropertySource(value = "classpath:redis.properties")
public class RedisConfig
{
    @Value("${port}")
    private int port;
    
    public int getPort()
    {
        return port;
    }
    
    public void setPort(int port)
    {
        this.port = port;
    }
}
//goto src\main\java\com\fly\demo\auto\ReloadConfig.java
package com.fly.demo.auto;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.springframework.context.annotation.Configuration;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 刷新配置文件
 * 
 * @author 00fly
 * @version [版本号, 2018年7月20日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
@Configuration
public class ReloadConfig
{
    private PropertiesConfiguration config;
    
    public ReloadConfig()
    {
        super();
        try
        {
            config = new PropertiesConfiguration("redis.properties");
            FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();
            strategy.setRefreshDelay(5000L);// 刷新周期5s
            config.setReloadingStrategy(strategy);
        }
        catch (ConfigurationException e)
        {
            log.error("刷新配置数据出错", e);
        }
    }
    
    /**
     * 刷新配置数据到对象中
     * 
     * @param redisConfig
     * @see [类、类#方法、类#成员]
     */
    public void refresh(RedisConfig redisConfig)
    {
        redisConfig.setPort(config.getInt("port"));
    }
}
//goto src\main\java\com\fly\demo\auto\task\MyTask1.java
package com.fly.demo.auto.task;

import java.util.Date;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * 定时任务类
 * 
 * @author 00fly
 * @version [版本号, 2017年4月25日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
@Component
public class MyTask1 implements SchedulingConfigurer
{
    PropertiesConfiguration config;
    
    public MyTask1()
    {
        super();
        try
        {
            config = new PropertiesConfiguration("demo.properties");
            FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();
            strategy.setRefreshDelay(60000L);// 刷新周期1分钟
            config.setReloadingStrategy(strategy);
        }
        catch (ConfigurationException e)
        {
            log.error(e.getMessage(), e.getCause());
        }
    }
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
    {
        taskRegistrar.addTriggerTask(new Runnable()
        {
            @Override
            public void run()
            {
                // 任务逻辑
                log.info("★★★★★★★ MyTask1 run ★★★★★★★");
            }
        }, new Trigger()
        {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext)
            {
                // 任务触发,可修改任务的执行周期
                String cron = config.getString("schedule.myjob.cron");
                return new CronTrigger(cron).nextExecutionTime(triggerContext);
            }
        });
    }
}
//goto src\main\java\com\fly\demo\auto\task\MyTask2.java
package com.fly.demo.auto.task;

import java.util.Date;
import java.util.ResourceBundle;

import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * 定时任务类
 * 
 * @author 00fly
 * @version [版本号, 2017年4月25日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
@Component
public class MyTask2 implements SchedulingConfigurer
{
    ResourceBundle config = ResourceBundle.getBundle("demo");
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
    {
        taskRegistrar.addTriggerTask(new Runnable()
        {
            @Override
            public void run()
            {
                // 任务逻辑
                log.info("------ MyTask2 run -------");
            }
        }, new Trigger()
        {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext)
            {
                // 任务触发,ResourceBundle方式读取修改任务的执行周期
                ResourceBundle.clearCache();
                String cron = config.getString("schedule.myjob.cron");
                return new CronTrigger(cron).nextExecutionTime(triggerContext);
            }
        });
    }
}
//goto src\main\java\com\fly\demo\auto\task\MyTask3.java
package com.fly.demo.auto.task;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;

import org.apache.commons.io.IOUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

/**
 * 定时任务类
 * 
 * @author 00fly
 * @version [版本号, 2017年4月25日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
@Component
public class MyTask3 implements SchedulingConfigurer
{
    Resource resource = new ClassPathResource("cron");
    
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar)
    {
        taskRegistrar.addTriggerTask(new Runnable()
        {
            @Override
            public void run()
            {
                // 任务逻辑
                log.info("######### MyTask3 run #########");
            }
        }, new Trigger()
        {
            @Override
            public Date nextExecutionTime(TriggerContext triggerContext)
            {
                String cronText = "*/4 * * * * ?";
                try
                {
                    cronText = IOUtils.toString(resource.getURL(), StandardCharsets.UTF_8);
                    log.info("resource url = {}, cronText = {}", resource.getURL(), cronText);
                }
                catch (IOException e)
                {
                    log.error(e.getMessage(), e.getCause());
                }
                // 任务触发,IO方式读取可修改任务的执行周期
                return new CronTrigger(cronText).nextExecutionTime(triggerContext);
            }
        });
    }
}
//goto src\main\resources\demo.properties
schedule.myjob.cron = */5 * * * * ?
//goto src\main\resources\redis.properties
port=8081

2. 神奇代码备份恢复工具源码备份

//goto pom.xml
<project xmlns="http://maven.apache/POM/4.0.0"
	xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.fly</groupId>
	<artifactId>procode</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>procode</name>
	<url>http://maven.apache.org</url>
	<properties>
		<java.version>1.8</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<!-- 导入swt -->
		<dependency>
			<groupId>org.eclipse.platform</groupId>
			<artifactId>org.eclipse.swt.win32.win32.x86_64</artifactId>
			<version>3.112.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.eclipse.platform</groupId>
					<artifactId>org.eclipse.swt.gtk.linux.aarch64</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.eclipse.platform</groupId>
					<artifactId>org.eclipse.swt.gtk.linux.arm</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- 导入jface -->
		<dependency>
			<groupId>org.eclipse.platform</groupId>
			<artifactId>org.eclipse.jface</artifactId>
			<version>3.17.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.eclipse.platform</groupId>
					<artifactId>org.eclipse.swt</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.eclipse.platform</groupId>
			<artifactId>org.eclipse.equinox.common</artifactId>
			<version>3.11.0</version>
		</dependency>
		<dependency>
			<groupId>org.eclipse.platform</groupId>
			<artifactId>org.eclipse.core.commands</artifactId>
			<version>3.9.800</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.7.0</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>
//goto src\main\java\com\fly\procode\core\DateUtil.java
package com.fly.procode.core;

import java.text.SimpleDateFormat;

public class DateUtil
{
    public final static String MMDDHHMM = "_MMddHHmm";
    
    public static final String getCurrDateTimeStr(String formatStr)
    {
        SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
        return sdf.format(System.currentTimeMillis());
    }
    
    public static final String getCurrDateTimeStr()
    {
        return getCurrDateTimeStr(MMDDHHMM);
    }
}
//goto src\main\java\com\fly\procode\core\Service.java
package com.fly.procode.core;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;

import com.fly.procode.process.RunProgress;

/**
 * 核心业务逻辑类
 */
public class Service extends Observable
{
    // 项目源代码目录
    private String sourcePath;
    
    private RunProgress runProcess;
    
    // 构造函数
    public Service()
    {
        super();
    }
    
    public RunProgress getRunProcess()
    {
        return runProcess;
    }
    
    public void setRunProcess(RunProgress runProcess)
    {
        this.runProcess = runProcess;
    }
    
    public String getSourcePath()
    {
        return sourcePath;
    }
    
    public void setSourcePath(String sourcePath)
    {
        this.sourcePath = sourcePath;
    }
    
    // 创建备份文件
    public void createBakFile(String bootPath, String bakFileName, List<String> fileFilter, String pwdtext)
    {
        // InputStream,OutputStream 并不负责文件创建或删除
        // 这部分功能由File来实现
        File bakfile = new File(bakFileName);
        if (bakfile.exists())
        {
            bakfile.delete();
        }
        if (!"".equals(pwdtext))
        {
            // new FileOutputStream(File file,boolean append)
            try (OutputStream fos = new FileOutputStream(bakFileName, false); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, "ISO-8859-1")))
            {
                writer.write("It is a encrypt backup File");
                writer.newLine();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        // 备份文件
        bakFile(bootPath, bakFileName, fileFilter, pwdtext);
    }
    
    // 递归备份文件
    private void bakFile(String bootPath, String bakFileName, List<String> fileFilter, String pwdtext)
    {
        File file = new File(sourcePath);
        if (file.exists() && file.isDirectory() && !file.getName().startsWith("."))
        {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++)
            {
                File f1 = files[i];
                if (f1.isDirectory())
                {
                    Service p = new Service();
                    p.setRunProcess(runProcess);
                    p.addObserver(runProcess);
                    p.setSourcePath(f1.getPath());
                    p.bakFile(bootPath, bakFileName, fileFilter, pwdtext);
                }
                else if (f1.isFile())
                {
                    if (f1.getPath().contains("\\target\\"))
                    {
                        return;
                    }
                    if (isExtraFile(f1.getName(), fileFilter))
                    {
                        setChanged();
                        notifyObservers("开始处理文件: " + f1.getName());
                        List<String> list = new ArrayList<String>();
                        String text = "//goto " + f1.getPath().substring(bootPath.length());
                        list.add(text);
                        list.addAll(getFiletext(f1.getPath()));
                        writeFile(list, bakFileName, pwdtext);
                    }
                }
            }
        }
    }
    
    // 以append 方式将text 写入 bakFile
    private void writeFile(List<String> list, String bakFileName, String pwdtext)
    {
        // 设置缓冲区大小为8192 bytes
        try (OutputStream os = new FileOutputStream(bakFileName, true); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "ISO-8859-1"), 8192))
        {
            for (String text : list)
            {
                writer.write(text);
                writer.newLine();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
    
    // 获取文件内容
    private List<String> getFiletext(String filePath)
    {
        // 设置缓冲区大小为8192 bytes
        List<String> list = new ArrayList<String>();
        try (InputStream is = new FileInputStream(filePath); BufferedReader in = new BufferedReader(new InputStreamReader(is, "ISO-8859-1"), 8192))
        {
            String text;
            while ((text = in.readLine()) != null)
            {
                list.add(text);
            }
            return list;
        }
        catch (IOException e)
        {
            e.printStackTrace();
            return null;
        }
    }
    
    // 是否为需要备份的文件类型
    private boolean isExtraFile(String fileName, List<String> fileFilter)
    {
        for (String postfix : fileFilter)
        {
            if (fileName.endsWith(postfix))
            {
                return true;
            }
        }
        return false;
    }
    
    // 从备份文件恢复文件至dir
    public void createSourceFile(String bakFile, String dir)
    {
        File f = new File(bakFile);
        String separator = System.getProperty("file.separator");
        int beginIndex = bakFile.lastIndexOf(separator) + 1;
        int endIndex = bakFile.indexOf("_");
        String t;
        if (endIndex > 0)
        {
            t = bakFile.substring(beginIndex, endIndex) + separator;
        }
        else
        {
            t = bakFile.substring(beginIndex, bakFile.indexOf(".")) + separator;
        }
        dir = dir + t;
        List<String> list = getFiletext(f.getPath());
        BufferedWriter writer = null;
        for (String text : list)
        {
            try
            {
                if (text.trim().startsWith("//goto "))
                {
                    // close old file
                    if (writer != null)
                    {
                        writer.close();
                    }
                    // creat new file
                    int pos = text.indexOf("//goto ") + 7;
                    File file = new File(dir + text.substring(pos));
                    file.getParentFile().mkdirs();
                    OutputStream os = new FileOutputStream(file);
                    writer = new BufferedWriter(new OutputStreamWriter(os, "8859_1"), 8192);
                }
                else
                {
                    if (writer != null)
                    {
                        writer.write(text);
                        writer.newLine();
                    }
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        try
        {
            if (writer != null)
            {
                writer.close();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
//goto src\main\java\com\fly\procode\process\RunProgress.java
package com.fly.procode.process;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;

import com.fly.procode.core.Service;

/**
 * 
 * 创建代码进度条线程
 * 
 * @author 00fly
 * @version [版本号, 2017年5月3日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class RunProgress implements IRunnableWithProgress, Observer
{
    private String sourcePath;
    
    private String bakFileName;
    
    private List<String> fileFilter;
    
    private String pwdtext;
    
    private IProgressMonitor monitor;
    
    public RunProgress(String bootPath, String bakFileName, List<String> fileFilter, String pwdtext)
    {
        super();
        this.sourcePath = bootPath;
        this.bakFileName = bakFileName;
        this.fileFilter = fileFilter;
        this.pwdtext = pwdtext;
    }
    
    @Override
    public void run(IProgressMonitor monitor)
        throws InvocationTargetException, InterruptedException
    {
        // 在当前目录,创建并运行脚本
        try
        {
            monitor.beginTask("代码备份", IProgressMonitor.UNKNOWN);
            monitor.subTask("代码备份中......");
            creatAndRun(sourcePath, bakFileName, fileFilter, pwdtext, monitor);
            monitor.done();
        }
        catch (Exception e)
        {
            throw new InvocationTargetException(e.getCause(), e.getMessage());
        }
    }
    
    // 运行代码创建程序
    private void creatAndRun(String sourcePath, String bakFileName, List<String> fileFilter, String pwdtext, IProgressMonitor monitor)
    {
        this.monitor = monitor;
        Service service = new Service();
        service.setRunProcess(this);
        service.addObserver(this);
        service.setSourcePath(sourcePath);
        service.createBakFile(sourcePath, bakFileName, fileFilter, pwdtext);
    }
    
    @Override
    public void update(Observable observable, Object msg)
    {
        if (msg instanceof String)
        {
            monitor.subTask((String)msg);
        }
    }
}
//goto src\main\java\com\fly\procode\ui\ProCodeTool.java
package com.fly.procode.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import com.fly.procode.core.DateUtil;
import com.fly.procode.core.Service;

public class ProCodeTool extends JFrame
{
    private static final long serialVersionUID = 965289280779529771L;
    
    // 设置窗口风格的菜单
    JCheckBoxMenuItem windows = new JCheckBoxMenuItem("Windows", true);
    
    JCheckBoxMenuItem metal = new JCheckBoxMenuItem("Metal");
    
    // 构造
    public ProCodeTool()
    {
        // 加载图标
        URL imgURL = this.getClass().getResource("/resource/icon.gif");
        if (imgURL != null)
        {
            Image image = Toolkit.getDefaultToolkit().createImage(imgURL);
            setIconImage(image);
        }
        setTitle("代码备份恢复工具 V1.1.0");
        // 设置宽高
        setSize(540, 340);
        // 得到当前系统screenSize
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        // 得到当前frameSize
        Dimension frameSize = this.getSize();
        
        // 自适应处理
        if (frameSize.height > screenSize.height)
        {
            frameSize.height = screenSize.height;
        }
        if (frameSize.width > screenSize.width)
        {
            frameSize.width = screenSize.width;
        }
        
        setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
        addMenu();
        add(new TabPanePanel());
        setWindowStyle("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        setResizable(false);
        setVisible(true);
        // 窗口关闭监听器
        addWindowListener(new WindowAdapter()
        {
            @Override
            public void windowClosing(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }
    
    // Menu set
    private void addMenu()
    {
        JMenuBar mb = new JMenuBar();
        // 菜单1
        JMenu windowStyle = new JMenu("窗口风格");
        windows.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent actionevent)
            {
                String plaf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
                setWindowStyle(plaf);
                windows.setState(true);
                metal.setState(false);
            }
        });
        
        metal.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent actionevent)
            {
                String plaf = "javax.swing.plaf.metal.MetalLookAndFeel";
                setWindowStyle(plaf);
                windows.setState(false);
                metal.setState(true);
            }
        });
        JMenuItem exit = new JMenuItem("退出");
        exit.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                System.exit(0);
            }
        });
        
        windowStyle.add(windows);
        windowStyle.add(metal);
        windowStyle.addSeparator();
        windowStyle.add(exit);
        mb.add(windowStyle);
        
        // 菜单2
        JMenu help = new JMenu("帮助");
        JMenuItem use = new JMenuItem("使用指南");
        use.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                JOptionPane.showMessageDialog(null,
                    "请按以下顺序操作:" + "\n 备份" + "\n 1. 选择项目源文件的目录。" + "\n 2. 选择需要备份的文件类型。" + "\n 3. 选择备份文件输出目录。" + "\n 4. 导出文件。" + "\n " + "\n 恢复 " + "\n 1. 到备份目录下选择备份文件。" + "\n 2. 选择备份文件的恢复目录。" + "\n 3. 恢复文件",
                    "使用指南",
                    JOptionPane.INFORMATION_MESSAGE);
            }
        });
        
        JMenuItem about = new JMenuItem("关于工具");
        about.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                JOptionPane.showMessageDialog(null, "代码备份恢复工具,00fly 于2009年9月。", "关于本工具", JOptionPane.INFORMATION_MESSAGE);
            }
        });
        help.add(use);
        help.addSeparator();
        help.add(about);
        mb.add(help);
        setJMenuBar(mb);
    }
    
    // 根据字串设置窗口外观
    private void setWindowStyle(String plaf)
    {
        try
        {
            // 设定用户界面的外观
            UIManager.setLookAndFeel(plaf);
            // 将用户界面改成当前设定的外观
            SwingUtilities.updateComponentTreeUI(this);
        }
        catch (Exception e)
        {
        }
    }
    
    // 对话框
    class TheDialogAdapter extends WindowAdapter
    {
        Dialog d;
        
        public void init(Dialog d)
        {
            this.d = d;
        }
        
        @Override
        public void windowClosing(WindowEvent e)
        {
            d.setVisible(false);
        }
    }
    
    // 卡片
    class TabPanePanel extends JPanel implements ChangeListener
    {
        private static final long serialVersionUID = 7226393795721621635L;
        
        JTabbedPane jtp;
        
        BakPanl p1;
        
        RestorePanl p2;
        
        TabPanePanel()
        {
            jtp = new JTabbedPane();
            p1 = new BakPanl();
            p1.setVisible(true);
            p2 = new RestorePanl();
            p2.setVisible(true);
            jtp.addTab(" 代码备份 ", p1);
            jtp.addTab(" 代码恢复 ", p2);
            jtp.addChangeListener(this);
            setLayout(new BorderLayout());
            add(jtp, BorderLayout.CENTER);
        }
        
        @Override
        public void stateChanged(ChangeEvent e)
        {
            if (e.getSource() == jtp)
            {
                int i = ((JTabbedPane)e.getSource()).getSelectedIndex();
                if (i == 0)
                {
                    p1.setVisible(true);
                    p2.setVisible(false);
                }
                else
                {
                    p1.setVisible(false);
                    p2.setVisible(true);
                }
            }
        }
    }
    
    // 备份面板
    class BakPanl extends JPanel
    {
        
        private static final long serialVersionUID = 4050369107824321460L;
        
        // 界面组件
        JLabel sourceLabel = new JLabel("项目源文件目录:");
        
        JTextField tsourcePath = new JTextField(null, 45);
        
        JButton sourceBrowse = new JButton("选择");
        
        JLabel fileTypeLabel = new JLabel("源文件类型:");
        
        JCheckBox jb1 = new JCheckBox(".java", true);
        
        JCheckBox jb2 = new JCheckBox(".jsp", true);
        
        JCheckBox jb3 = new JCheckBox(".xml", true);
        
        JCheckBox jb4 = new JCheckBox(".properties", true);
        
        JCheckBox jb5 = new JCheckBox(".classpath");
        
        JCheckBox jb6 = new JCheckBox(".project");
        
        JCheckBox jb7 = new JCheckBox(".mymetadata");
        
        JCheckBox jb8 = new JCheckBox(".sql");
        
        JCheckBox jb9 = new JCheckBox(".htm");
        
        JCheckBox jb10 = new JCheckBox(".html");
        
        JCheckBox jb11 = new JCheckBox(".vm");
        
        JLabel pwdLabel = new JLabel("可选项:");
        
        JButton pwdButton = new JButton("设置密码");
        
        JTextField tpwd = new JTextField(null, 10);
        
        JLabel bakeLabel = new JLabel("备份至目录:");
        
        JTextField tbakPath = new JTextField(null, 45);
        
        JButton bakBrowse = new JButton("选择");
        
        JButton bakButton = new JButton("开始备份");
        
        public BakPanl()
        {
            super();
            setLayout(null);
            setBackground(Color.WHITE);
            // 项目源文件路径
            sourceLabel.setBounds(20, 30, 120, 18);
            add(sourceLabel);
            tsourcePath.setFocusable(false);
            tsourcePath.setBounds(120, 30, 300, 20);
            add(tsourcePath);
            sourceBrowse.setBounds(430, 30, 70, 20);
            
            sourceBrowse.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent arg0)
                {
                    JFileChooser fc = new JFileChooser();
                    fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                    if (fc.showOpenDialog(null) == 1)
                    {
                        return;
                    }
                    else
                    {
                        // 新源文件目录被选中,清空密码
                        tpwd.setText(null);
                        File f = fc.getSelectedFile();
                        if (f.getPath().endsWith("\\"))
                        {
                            tsourcePath.setText(f.getAbsolutePath());
                        }
                        else
                        {
                            tsourcePath.setText(f.getAbsolutePath() + "\\");
                        }
                        pwdButton.setEnabled(true);
                    }
                }
            });
            add(sourceBrowse);
            
            // 文件类型
            fileTypeLabel.setBounds(20, 60, 120, 18);
            add(fileTypeLabel);
            jb1.setBounds(120, 60, 120, 18);
            jb1.setBackground(Color.WHITE);
            jb2.setBounds(120, 80, 120, 18);
            jb2.setBackground(Color.WHITE);
            jb3.setBounds(120, 100, 120, 18);
            jb3.setBackground(Color.WHITE);
            jb4.setBounds(120, 120, 120, 18);
            jb4.setBackground(Color.WHITE);
            jb5.setBounds(250, 60, 120, 18);
            jb5.setBackground(Color.WHITE);
            jb6.setBounds(250, 80, 120, 18);
            jb6.setBackground(Color.WHITE);
            jb7.setBounds(250, 100, 120, 18);
            jb7.setBackground(Color.WHITE);
            jb8.setBounds(250, 120, 120, 18);
            jb8.setBackground(Color.WHITE);
            jb9.setBounds(380, 60, 120, 18);
            jb9.setBackground(Color.WHITE);
            jb10.setBounds(380, 80, 120, 18);
            jb10.setBackground(Color.WHITE);
            jb11.setBounds(380, 100, 120, 18);
            jb11.setBackground(Color.WHITE);
            add(jb1);
            add(jb2);
            add(jb3);
            add(jb4);
            add(jb5);
            add(jb6);
            add(jb7);
            add(jb8);
            add(jb9);
            add(jb10);
            add(jb11);
            
            // 密码项
            pwdLabel.setBounds(20, 150, 120, 18);
            add(pwdLabel);
            pwdButton.setBounds(120, 150, 100, 20);
            pwdButton.setEnabled(false);
            pwdButton.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent arg0)
                {
                    try
                    {
                        String pwd = JOptionPane.showInputDialog(null, "请输入8位密码(包括字母,数字和字符):", "带密码备份", JOptionPane.INFORMATION_MESSAGE).trim();
                        if (!"".equals(pwd.trim()) && pwd.length() == 8)
                        {
                            // 设置密码
                            tpwd.setText(pwd);
                            JOptionPane.showMessageDialog(null, "密码设置成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
                        }
                        else
                        {
                            // 清空密码
                            tpwd.setText(null);
                            JOptionPane.showMessageDialog(null, "密码设置失败!", "警告", JOptionPane.WARNING_MESSAGE);
                        }
                    }
                    catch (Exception e)
                    {
                        // 清空密码
                        tpwd.setText(null);
                        JOptionPane.showMessageDialog(null, "密码设置失败!", "警告", JOptionPane.WARNING_MESSAGE);
                    }
                }
            });
            add(pwdButton);
            add(tpwd);
            
            // 备份目录
            bakeLabel.setBounds(20, 180, 120, 18);
            add(bakeLabel);
            tbakPath.setBounds(120, 180, 300, 20);
            tbakPath.setFocusable(false);
            tbakPath.setText(new File(" ").getAbsolutePath().trim());
            add(tbakPath);
            bakBrowse.setBounds(430, 180, 70, 20);
            bakBrowse.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent arg0)
                {
                    JFileChooser fc = new JFileChooser(new File("").getAbsoluteFile());
                    fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                    if (fc.showOpenDialog(null) == 1)
                    {
                        return;
                    }
                    else
                    {
                        File f = fc.getSelectedFile();
                        if (f.getPath().endsWith("\\"))
                        {
                            tbakPath.setText(f.getAbsolutePath());
                        }
                        else
                        {
                            tbakPath.setText(f.getAbsolutePath() + "\\");
                        }
                    }
                }
            });
            add(bakBrowse);
            
            bakButton.setBounds(220, 220, 100, 30);
            bakButton.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent arg0)
                {
                    if (!"".equals(tsourcePath.getText()))
                    {
                        String sourcePath = tsourcePath.getText();
                        String bakFileName = tbakPath.getText() + new File(sourcePath).getName() + DateUtil.getCurrDateTimeStr() + ".bak";
                        List<String> list = new ArrayList<String>();
                        if (jb1.isSelected())
                        {
                            list.add(".java");
                        }
                        if (jb2.isSelected())
                        {
                            list.add(".jsp");
                        }
                        if (jb3.isSelected())
                        {
                            list.add(".xml");
                        }
                        if (jb4.isSelected())
                        {
                            list.add(".properties");
                        }
                        if (jb5.isSelected())
                        {
                            list.add(".classpath");
                        }
                        if (jb6.isSelected())
                        {
                            list.add(".project");
                        }
                        if (jb7.isSelected())
                        {
                            list.add(".mymetadata");
                        }
                        if (jb8.isSelected())
                        {
                            list.add(".sql");
                        }
                        if (jb9.isSelected())
                        {
                            list.add(".htm");
                        }
                        if (jb10.isSelected())
                        {
                            list.add(".html");
                        }
                        if (jb11.isSelected())
                        {
                            list.add(".vm");
                        }
                        try
                        {
                            Service service = new Service();
                            String pwdtext = tpwd.getText();
                            service.setSourcePath(sourcePath);
                            service.createBakFile(sourcePath, bakFileName, list, pwdtext);
                            String message = "创建明文备份文件 " + bakFileName + " 成功!";
                            
                            if (!"".equals(pwdtext))
                            {
                                message = "创建加密备份文件 " + bakFileName + " 成功!";
                            }
                            JOptionPane.showMessageDialog(null, message);
                        }
                        catch (RuntimeException e)
                        {
                            e.printStackTrace();
                            JOptionPane.showMessageDialog(null, "创建备份文件 " + bakFileName + " 失败!", "错误", JOptionPane.ERROR_MESSAGE);
                        }
                    }
                    else
                    {
                        JOptionPane.showMessageDialog(null, "项目源文件目录不可为空!", "警告", JOptionPane.WARNING_MESSAGE);
                        return;
                    }
                }
            });
            add(bakButton);
        }
    }
    
    // 恢复面板
    class RestorePanl extends JPanel
    {
        
        private static final long serialVersionUID = -4960976746526411789L;
        
        // 界面组件
        JLabel bakeLabel = new JLabel("选择备份文件:");
        
        JTextField tbakPath = new JTextField(null, 45);
        
        JButton bakBrowse = new JButton("选择");
        
        JLabel sourceLabel = new JLabel("恢复至目录:");
        
        JTextField tsourcePath = new JTextField(null, 45);
        
        JButton sourceBrowse = new JButton("选择");
        
        JButton resetButton = new JButton("开始恢复");
        
        public RestorePanl()
        {
            super();
            setLayout(null);
            // Label
            bakeLabel.setBounds(20, 30, 120, 18);
            add(bakeLabel);
            // Text
            tbakPath.setBounds(120, 30, 300, 20);
            tbakPath.setFocusable(false);
            add(tbakPath);
            // Button
            bakBrowse.setBounds(430, 30, 70, 20);
            bakBrowse.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent arg0)
                {
                    JFileChooser fc = new JFileChooser(new File("").getAbsoluteFile());
                    fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
                    if (fc.showOpenDialog(null) == 1)
                    {
                        return;
                    }
                    else
                    {
                        File f = fc.getSelectedFile();
                        tbakPath.setText(f.getAbsolutePath());
                    }
                }
            });
            add(bakBrowse);
            
            // Label
            sourceLabel.setBounds(20, 100, 120, 18);
            add(sourceLabel);
            // Text
            tsourcePath.setBounds(120, 100, 300, 20);
            tsourcePath.setFocusable(false);
            tsourcePath.setText(new File(" ").getAbsolutePath().trim());
            add(tsourcePath);
            // Button
            sourceBrowse.setBounds(430, 100, 70, 20);
            sourceBrowse.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent arg0)
                {
                    JFileChooser fc = new JFileChooser();
                    fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                    if (fc.showOpenDialog(null) == 1)
                    {
                        return;
                    }
                    else
                    {
                        File f = fc.getSelectedFile();
                        if (f.getPath().endsWith("\\"))
                        {
                            tsourcePath.setText(f.getAbsolutePath());
                        }
                        else
                        {
                            tsourcePath.setText(f.getAbsolutePath() + "\\");
                        }
                    }
                }
            });
            add(sourceBrowse);
            
            // 恢复
            resetButton.setBounds(220, 170, 100, 30);
            resetButton.addActionListener(new ActionListener()
            {
                @Override
                public void actionPerformed(ActionEvent arg0)
                {
                    if ("".equals(tbakPath.getText()) || "".equals(tsourcePath.getText()))
                    {
                        JOptionPane.showMessageDialog(null, "备份文件或恢复目录不可为空!", "警告", JOptionPane.WARNING_MESSAGE);
                        return;
                    }
                    else
                    {
                        String dir = tsourcePath.getText();
                        try
                        {
                            new Service().createSourceFile(tbakPath.getText(), dir);
                            JOptionPane.showMessageDialog(null, "恢复文件到目录: " + dir + " 成功!");
                        }
                        catch (RuntimeException e)
                        {
                            JOptionPane.showMessageDialog(null, "恢复文件失败,请重新检查备份文件参数!", "错误", JOptionPane.ERROR_MESSAGE);
                        }
                    }
                }
            });
            add(resetButton);
        }
    }
    
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> new ProCodeTool());
    }
}
//goto src\main\java\com\fly\procode\ui\ProCodeToolSimple.java
package com.fly.procode.ui;

import java.awt.BorderLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
 * 
 * 工具UI简化版本
 * 
 * @author 00fly
 * @version [版本号, 2023年3月3日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class ProCodeToolSimple extends JFrame
{
    private static final long serialVersionUID = -2145267777297657212L;
    
    JFrame frame = this;
    
    public ProCodeToolSimple()
    {
        initComponent();
        this.setVisible(true);
        this.setResizable(true);
        this.setAlwaysOnTop(true);
        this.setTitle("代码恢复工具 V1.0");
        this.setBounds(400, 200, 1200, 550);
        this.setDefaultCloseOperation(EXIT_ON_CLOSE);
        try
        {
            // 设定用户界面的外观
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
            SwingUtilities.updateComponentTreeUI(this);
        }
        catch (Exception e)
        {
        }
    }
    
    /**
     * 组件初始化
     * 
     * @see [类、类#方法、类#成员]
     */
    private void initComponent()
    {
        // 加载图标
        this.setIconImage(getToolkit().createImage(getClass().getResource("/image/icon.gif")));
        
        JTextArea textArea = new JTextArea();
        textArea.setToolTipText("请输入全部待恢复代码");
        
        // JTextArea不自带滚动条,因此就需要把文本区放到一个滚动窗格中
        JScrollPane scroll = new JScrollPane(textArea);
        scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
        
        JButton button = new JButton("开始恢复代码");
        this.add(scroll, BorderLayout.CENTER);
        this.add(button, BorderLayout.SOUTH);
        button.addMouseListener(new MouseAdapter()
        {
            @Override
            public void mouseClicked(MouseEvent event)
            {
                try
                {
                    String dir = new File(" ").getCanonicalPath();
                    createSourceFile(textArea.getText(), dir);
                    JOptionPane.showMessageDialog(frame, "恢复文件到目录: " + dir + " 成功!", "提示", JOptionPane.INFORMATION_MESSAGE);
                }
                catch (Exception e)
                {
                    JOptionPane.showMessageDialog(frame, e.getMessage(), "系統异常", JOptionPane.ERROR_MESSAGE);
                }
            }
        });
    }
    
    /**
     * 恢复文件至dir
     * 
     * @param text
     * @param dir
     */
    private void createSourceFile(String text, String dir)
    {
        String[] textArr = text.split("\n");
        BufferedWriter writer = null;
        for (String line : textArr)
        {
            try
            {
                if (line.trim().startsWith("//goto "))
                {
                    // close old file
                    if (writer != null)
                    {
                        writer.close();
                    }
                    // creat new file
                    int pos = line.indexOf("//goto ") + 7;
                    File file = new File(dir + line.substring(pos));
                    file.getParentFile().mkdirs();
                    OutputStream os = new FileOutputStream(file);
                    writer = new BufferedWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8), 8192);
                }
                else
                {
                    if (writer != null)
                    {
                        writer.write(line);
                        writer.newLine();
                    }
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        try
        {
            if (writer != null)
            {
                writer.close();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(() -> new ProCodeToolSimple());
    }
}
//goto src\main\java\com\fly\procode\ui\ProCodeToolSJ.java
package com.fly.procode.ui;

import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;

import com.fly.procode.core.DateUtil;
import com.fly.procode.core.Service;
import com.fly.procode.process.RunProgress;

/**
 * 
 * swt jface版本
 * 
 * @author 00fly
 * @version [版本号, 2020年4月24日]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class ProCodeToolSJ
{
    Display display;
    
    Shell shell;
    
    org.eclipse.swt.widgets.List listJava;
    
    org.eclipse.swt.widgets.List listPage;
    
    public ProCodeToolSJ()
    {
        super();
        display = new Display();
        shell = new Shell(display, SWT.MIN | SWT.CLOSE | SWT.ON_TOP);
        InputStream is = this.getClass().getResourceAsStream("/image/icon.gif");
        if (is != null)
        {
            shell.setImage(new Image(display, is));
        }
        shell.setText("代码备份恢复工具 V1.1.0");
        shell.setSize(540, 383);
        Rectangle screeRec = display.getBounds();
        Rectangle shellRec = shell.getBounds();
        if (shellRec.height > screeRec.height)
        {
            shellRec.height = screeRec.height;
        }
        if (shellRec.width > screeRec.width)
        {
            shellRec.width = screeRec.width;
        }
        shell.setLocation((screeRec.width - shellRec.width) / 2, (screeRec.height - shellRec.height) / 2);
        addMenu();
        setContent();
        shell.open();
        while (!shell.isDisposed())
        {
            if (!display.readAndDispatch())
            {
                display.sleep();
            }
        }
        display.dispose();
    }
    
    // main
    public static void main(String[] args)
    {
        new ProCodeToolSJ();
    }
    
    // Menu set
    private void addMenu()
    {
        Menu m = new Menu(shell, SWT.BAR);
        // create a file menu and add an exit item
        MenuItem file = new MenuItem(m, SWT.CASCADE);
        file.setText("文件(&F)");
        file.setAccelerator(SWT.CTRL + 'F');
        Menu filemenu = new Menu(shell, SWT.DROP_DOWN);
        file.setMenu(filemenu);
        MenuItem openMenuItem = new MenuItem(filemenu, SWT.CASCADE);
        openMenuItem.setText("最近备份(&O)");
        openMenuItem.setAccelerator(SWT.CTRL + 'O');
        Menu submenu = new Menu(shell, SWT.DROP_DOWN);
        openMenuItem.setMenu(submenu);
        MenuItem childItem = new MenuItem(submenu, SWT.PUSH);
        childItem.setText("Child");
        MenuItem saveMenuItem = new MenuItem(filemenu, SWT.CASCADE);
        saveMenuItem.setText("最近恢复(&S)");
        saveMenuItem.setAccelerator(SWT.CTRL + 'S');
        Menu submenu2 = new Menu(shell, SWT.DROP_DOWN);
        saveMenuItem.setMenu(submenu2);
        MenuItem childItem2 = new MenuItem(submenu2, SWT.PUSH);
        childItem2.setText("Child2");
        new MenuItem(filemenu, SWT.SEPARATOR);
        MenuItem exitMenuItem = new MenuItem(filemenu, SWT.PUSH);
        exitMenuItem.setText("退出(&X)");
        exitMenuItem.setAccelerator(SWT.CTRL + 'X');
        exitMenuItem.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent e)
            {
                System.exit(0);
            }
        });
        // create a Help menu and add an about item
        MenuItem help = new MenuItem(m, SWT.CASCADE);
        help.setText("帮助(&H)");
        help.setAccelerator(SWT.CTRL + 'H');
        Menu helpmenu = new Menu(shell, SWT.DROP_DOWN);
        help.setMenu(helpmenu);
        MenuItem useMenuItem = new MenuItem(helpmenu, SWT.PUSH);
        useMenuItem.setText("使用指南(&U)");
        new MenuItem(helpmenu, SWT.SEPARATOR);
        MenuItem aboutMenuItem = new MenuItem(helpmenu, SWT.PUSH);
        aboutMenuItem.setText("关于工具(&A)");
        useMenuItem.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent event)
            {
                MessageDialog.openInformation(shell,
                    "使用指南",
                    "请按以下顺序操作:" + "\n 文件备份" + "\n 1. 选择项目源文件的目录。" + "\n 2. 选择需要备份的文件类型。" + "\n 3. 选择备份文件输出目录。" + "\n 4. 导出文件。" + "\n " + "\n 文件恢复 " + "\n 1. 到备份目录下选择备份文件。" + "\n 2. 选择备份文件的恢复目录。" + "\n 3. 恢复文件");
            }
        });
        aboutMenuItem.addSelectionListener(new SelectionAdapter()
        {
            @Override
            public void widgetSelected(SelectionEvent event)
            {
                MessageDialog.openInformation(shell, "关于本工具", "代码备份恢复工具。\n\n00fly 于2010年5月。");
            }
        });
        shell.setMenuBar(m);
    }
    
    public void setContent()
    {
        TabFolder tabFolder = new TabFolder(shell, SWT.NONE);
        tabFolder.setBounds(5, 2, 525, 328);
        TabItem item = new TabItem(tabFolder, SWT.NONE);
        item.setText(" 代码备份 ");
        item.setControl(new BakPanlTab(tabFolder, SWT.NONE));
        TabItem item2 = new TabItem(tabFolder, SWT.NONE);
        item2.setText(" 代码恢复 ");
        item2.setControl(new RestorePanlTab(tabFolder, SWT.NONE));
    }
    
    // 备份面板
    class BakPanlTab extends Composite
    {
        Button pwdButton;
        
        Text tpwd = new Text(this, SWT.NONE);
        
        public BakPanlTab(Composite c, int style)
        {
            super(c, style);
            Label sourceLabel = new Label(this, SWT.NONE);
            sourceLabel.setText("项目源文件目录:");
            sourceLabel.setBounds(20, 10, 100, 18);
            final Text tsourcePath = new Text(this, SWT.BORDER);
            tsourcePath.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
            tsourcePath.setEditable(false);
            tsourcePath.setBounds(120, 10, 300, 20);
            Button sourceBrowse = new Button(this, SWT.PUSH);
            sourceBrowse.setText("选择");
            sourceBrowse.setBounds(430, 10, 70, 20);
            sourceBrowse.addSelectionListener(new SelectionAdapter()
            {
                @Override
                public void widgetSelected(SelectionEvent event)
                {
                    DirectoryDialog dialog = new DirectoryDialog(shell);
                    dialog.setFilterPath(tsourcePath.getText());
                    String fileName = dialog.open();
                    if (fileName != null)
                    {
                        if (fileName.endsWith("\\"))
                        {
                            tsourcePath.setText(fileName);
                        }
                        else
                        {
                            tsourcePath.setText(fileName + "\\");
                        }
                        // 新源文件目录被选中,清空密码
                        tpwd.setText("");
                        pwdButton.setEnabled(true);
                    }
                }
            });
            Label fileTypeLabel = new Label(this, SWT.NONE);
            fileTypeLabel.setText("源文件类型:");
            fileTypeLabel.setBounds(20, 40, 100, 18);
            
            listJava = new org.eclipse.swt.widgets.List(this, SWT.BORDER | SWT.V_SCROLL | SWT.SIMPLE | SWT.MULTI);
            listJava.setBounds(120, 40, 120, 128);
            listJava.setToolTipText("选择需要备份的源文件类型,支持多选!");
            String[] fileTypes = {".java", ".xml", ".yml", ".yaml", ".properties", ".sql", ".md"};
            listJava.setItems(fileTypes);
            IntStream.range(0, fileTypes.length).forEach(index -> listJava.select(index));
            
            listPage = new org.eclipse.swt.widgets.List(this, SWT.BORDER | SWT.V_SCROLL | SWT.SIMPLE | SWT.MULTI);
            listPage.setBounds(280, 40, 120, 128);
            listPage.setToolTipText("选择需要备份的源文件类型,支持多选!");
            String[] fileTypes2 = {"Dockerfile", ".htm", ".html", ".js", ".css", ".vue", ".jsp", ".sh"};
            listPage.setItems(fileTypes2);
            IntStream.range(0, fileTypes2.length).forEach(index -> listPage.select(index));
            
            Label pwdLabel = new Label(this, SWT.NONE);
            pwdLabel.setText("可选项:");
            pwdLabel.setBounds(20, 180, 100, 18);
            pwdButton = new Button(this, SWT.PUSH);
            pwdButton.setText("设置密码");
            pwdButton.setBounds(120, 180, 100, 20);
            pwdButton.setEnabled(false);
            pwdButton.addSelectionListener(new SelectionAdapter()
            {
                @Override
                public void widgetSelected(SelectionEvent event)
                {
                    InputDialog dlg = new InputDialog(shell, "带密码备份", "请输入8位密码(包括字母,数字和字符):", "", new LengthValidator());
                    if (dlg.open() == Window.OK)
                    {
                        tpwd.setText(dlg.getValue());
                        MessageDialog.openInformation(shell, "恭喜你", "密码设置成功!");
                    }
                }
            });
            Label bakeLabel = new Label(this, SWT.NONE);
            bakeLabel.setText("备份至目录:");
            bakeLabel.setBounds(20, 210, 100, 18);
            final Text tbakPath = new Text(this, SWT.BORDER);
            tbakPath.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
            tbakPath.setEditable(false);
            tbakPath.setText(new File(" ").getAbsolutePath().trim());
            tbakPath.setBounds(120, 210, 300, 20);
            Button bakBrowse = new Button(this, SWT.PUSH);
            bakBrowse.setText("选择");
            bakBrowse.setBounds(430, 210, 70, 20);
            bakBrowse.addSelectionListener(new SelectionAdapter()
            {
                @Override
                public void widgetSelected(SelectionEvent event)
                {
                    DirectoryDialog dialog = new DirectoryDialog(shell);
                    dialog.setFilterPath(tbakPath.getText());
                    String fileName = dialog.open();
                    if (fileName != null)
                    {
                        if (fileName.endsWith("\\"))
                        {
                            tbakPath.setText(fileName);
                        }
                        else
                        {
                            tbakPath.setText(fileName + "\\");
                        }
                    }
                }
            });
            Button bakButton = new Button(this, SWT.PUSH);
            bakButton.setText("开始备份");
            bakButton.setBounds(220, 250, 100, 40);
            bakButton.addSelectionListener(new SelectionAdapter()
            {
                @Override
                public void widgetSelected(SelectionEvent event)
                {
                    if ("".equals(tsourcePath.getText()))
                    {
                        MessageDialog.openWarning(shell, "警告", "项目源文件目录不可为空!");
                        return;
                    }
                    if (listJava.getSelectionCount() + listPage.getSelectionCount() < 1)
                    {
                        MessageDialog.openError(shell, "警告", "请选择源文件类型,支持多选!");
                        listJava.setFocus();
                        return;
                    }
                    String sourcePath = tsourcePath.getText();
                    String bakFileName = tbakPath.getText() + new File(sourcePath).getName() + DateUtil.getCurrDateTimeStr() + ".bak";
                    try
                    {
                        List<String> fileTypes = new ArrayList<String>();
                        for (String it : listJava.getSelection())
                        {
                            fileTypes.add(it);
                        }
                        for (String it : listPage.getSelection())
                        {
                            fileTypes.add(it);
                        }
                        String pwdtext = tpwd.getText();
                        IRunnableWithProgress runnable = new RunProgress(sourcePath, bakFileName, fileTypes, pwdtext);
                        new ProgressMonitorDialog(shell).run(true, false, runnable);
                        String message = "创建明文备份文件 " + bakFileName + " 成功!";
                        if (!"".equals(pwdtext))
                        {
                            message = "创建加密备份文件 " + bakFileName + " 成功!";
                        }
                        MessageDialog.openInformation(shell, "提示", message);
                    }
                    catch (InvocationTargetException e)
                    {
                        MessageDialog.openError(shell, "警告", e.getMessage());
                    }
                    catch (InterruptedException e)
                    {
                        MessageDialog.openInformation(shell, "Cancelled", "刷新操作被用户取消!");
                    }
                    catch (RuntimeException e)
                    {
                        MessageDialog.openError(shell, "错误", "创建备份文件 " + bakFileName + " 失败!");
                    }
                    if (MessageDialog.openConfirm(shell, "查看备份文件", "处理完成,是否直接查看文件?"))
                    {
                        try
                        {
                            Desktop.getDesktop().open(new File(bakFileName).getParentFile());
                        }
                        catch (IOException e)
                        {
                        }
                    }
                }
            });
        }
        
        // 密码长度验证
        class LengthValidator implements IInputValidator
        {
            @Override
            public String isValid(String newText)
            {
                int len = newText.length();
                if (len < 8)
                {
                    return "长度少于8位";
                }
                if (len > 8)
                {
                    return "长度大于8位";
                }
                return null;
            }
        }
    }
    
    // 恢复面板
    class RestorePanlTab extends Composite
    {
        public RestorePanlTab(Composite c, int style)
        {
            super(c, style);
            Label bakeLabel = new Label(this, SWT.NONE);
            bakeLabel.setText("选择备份文件:");
            bakeLabel.setBounds(20, 30, 100, 18);
            final Text tbakPath = new Text(this, SWT.BORDER);
            tbakPath.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
            tbakPath.setEditable(false);
            tbakPath.setBounds(120, 30, 300, 20);
            Button bakBrowse = new Button(this, SWT.PUSH);
            bakBrowse.setText("选择");
            bakBrowse.setBounds(430, 30, 70, 20);
            bakBrowse.addSelectionListener(new SelectionAdapter()
            {
                @Override
                public void widgetSelected(SelectionEvent event)
                {
                    FileDialog fileDialog = new FileDialog(shell, SWT.OPEN);
                    fileDialog.setText("选择文件");
                    String[] filterExt = {"*.bak"};
                    fileDialog.setFilterExtensions(filterExt);
                    String selected = fileDialog.open();
                    if (selected == null)
                    {
                        return;
                    }
                    tbakPath.setText(selected);
                }
            });
            Label sourceLabel = new Label(this, SWT.NONE);
            sourceLabel.setText("恢复至目录:");
            sourceLabel.setBounds(20, 100, 100, 18);
            final Text tsourcePath = new Text(this, SWT.BORDER);
            tsourcePath.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
            tsourcePath.setEditable(false);
            tsourcePath.setText(new File(" ").getAbsolutePath().trim());
            tsourcePath.setBounds(120, 100, 300, 20);
            Button sourceBrowse = new Button(this, SWT.PUSH);
            sourceBrowse.setText("选择");
            sourceBrowse.setBounds(430, 100, 70, 20);
            sourceBrowse.addSelectionListener(new SelectionAdapter()
            {
                @Override
                public void widgetSelected(SelectionEvent event)
                {
                    DirectoryDialog dialog = new DirectoryDialog(shell);
                    dialog.setFilterPath(tsourcePath.getText());
                    String fileName = dialog.open();
                    if (fileName != null)
                    {
                        if (fileName.endsWith("\\"))
                        {
                            tsourcePath.setText(fileName);
                        }
                        else
                        {
                            tsourcePath.setText(fileName + "\\");
                        }
                    }
                }
            });
            Button resetButton = new Button(this, SWT.PUSH);
            resetButton.setText("开始恢复");
            resetButton.setBounds(220, 170, 100, 40);
            resetButton.addSelectionListener(new SelectionAdapter()
            {
                @Override
                public void widgetSelected(SelectionEvent event)
                {
                    if ("".equals(tbakPath.getText()) || "".equals(tsourcePath.getText()))
                    {
                        MessageDialog.openWarning(shell, "警告", "备份文件或恢复目录不可为空!");
                    }
                    else
                    {
                        String dir = tsourcePath.getText();
                        try
                        {
                            new Service().createSourceFile(tbakPath.getText(), dir);
                            MessageDialog.openInformation(shell, "提示", "恢复文件到目录: " + dir + " 成功!");
                        }
                        catch (RuntimeException e)
                        {
                            MessageDialog.openError(shell, "错误", "恢复文件失败,请重新检查备份文件!");
                        }
                    }
                }
            });
        }
    }
}

本文标签: 逸事操作指南备份神奇代码