SWT初学写的小工具

背景:我们项目中多语言文件是放在xml文件中,时间一长,一些多语言在代码中并没有用到,老大要我写个小工具将未使用到的多语言ID给找出来,需要考虑的就是:查找源代码文件的效率和判断ID是否被使用(注释中用到的不算),想用windows下的find findstr命令的,但java注释不好区分,因此还是只能对每个文件进行读取判断。

其中注释中出现ID的情况有以下几种情况:
1:// 开头的行,对应正则是 ^\\s*//.*
2:*开头的行,java的块注释中除首末两行其它是以*开头,对应正则是 ^\\s*\\*.*
3:/*开头的行,正则是 ^\\s*/\\*.*
4:*/结尾的行,正则 \\*/\\s*$
5:我把空行也认为是注释,正则 ^\\s+
第2种情况,有可能块注释中除首末外其它行并不以*开头,也是注释,因此这里比较麻烦,要在代码中判断。

代码如下:
public class FindStrInFiles implements Listener {
	private Text msrText;
	private Button msrBtn;
	private Text projectText;
	private Button projectBtn;
	private ProgressBar progressBar;
	private Button searchBtn;
	private Text resultText;
	private Label statusLabel;
	private Pattern commentPattern;
	private Matcher commentMatcher;
	private BufferedReader br;
	private String line;
	private boolean preComment = false;
	private Display display;
	private Shell shell;
	private LinkedList<String> msrList;
	private boolean isUsed;
	private WorkThread workThread;
	private boolean isStopped;
	private boolean isSuspended;

	public static void main(String... args) {
		FindStrInFiles find = new FindStrInFiles();
		find.init();
	}
	
	private void init() {
		// 以// * /*开头或者以*/结尾的都是注释,空行也认为是注释,
		// 还有一种情况是在块注释中间的行,在Pattern中无法判断,需要在代码中单独判断
		this.commentPattern = Pattern
				.compile("(^\\s*//.*)|(^\\s*\\*.*)|(^\\s*/\\*.*)|(\\*/\\s*$)|(^\\s+)");

		display = new Display();
		shell = new Shell(display);
		GridLayout layout = new GridLayout(3, false);
		layout.horizontalSpacing = 10;
		layout.marginLeft = layout.marginRight = layout.marginBottom = 5;
		shell.setLayout(layout);
		Label label = new Label(shell, SWT.NONE);
		label.setText("多语言文件");
		GridData gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false,
				false, 1, 1);
		label.setLayoutData(gridData);
		this.msrText = new Text(shell, SWT.BORDER | SWT.SINGLE);
		gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
		gridData.heightHint = 18;
		this.msrText.setLayoutData(gridData);
		this.msrBtn = new Button(shell, SWT.BORDER | SWT.NONE);
		this.msrBtn.setText("选择");
		gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
		gridData.widthHint = 50;
		this.msrBtn.setLayoutData(gridData);
		this.msrBtn.addListener(SWT.Selection, this);
		label = new Label(shell, SWT.NONE | SWT.UNDERLINE_SINGLE);
		label.setText("工程父目录");
		label.setForeground(display.getSystemColor(SWT.COLOR_BLUE));
		label.setToolTipText("该目录下的所有项目中src下的源代码都会被搜索,不是工程的文件夹被忽略");
		gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
		label.setLayoutData(gridData);
		this.projectText = new Text(shell, SWT.BORDER | SWT.SINGLE);
		gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
		gridData.heightHint = 18;
		this.projectText.setLayoutData(gridData);
		this.projectBtn = new Button(shell, SWT.BORDER);
		this.projectBtn.setText("选择");
		gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
		gridData.widthHint = 50;
		this.projectBtn.setLayoutData(gridData);
		this.projectBtn.addListener(SWT.Selection, this);
		this.progressBar = new ProgressBar(shell, SWT.SIMPLE);
		gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
		this.progressBar.setLayoutData(gridData);
		this.searchBtn = new Button(shell, SWT.BORDER);
		this.searchBtn.setText("搜索");
		this.searchBtn.addListener(SWT.Selection, this);
		gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
		gridData.widthHint = 50;
		this.searchBtn.setLayoutData(gridData);
		label = new Label(shell, SWT.NONE);
		label.setText("结果");
		gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, true, 1, 1);
		label.setLayoutData(gridData);
		this.resultText = new Text(shell, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL
				| SWT.V_SCROLL | SWT.READ_ONLY);
		gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1);
		FontData fontData = new FontData();
		fontData.setHeight(11);
		this.resultText.setFont(new Font(display, fontData));
		this.resultText.setLayoutData(gridData);
		this.statusLabel = new Label(shell, SWT.NONE);
		gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
		this.statusLabel.setLayoutData(gridData);

		shell.setBounds(100, 20, 800, 600);
		shell.setDefaultButton(this.msrBtn);
		shell.setText("搜索未使用的多语言ID");
		shell.addListener(SWT.Dispose, this);
		shell.open();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
		shell.dispose();
	}

	@Override
	public void handleEvent(Event event) {
		int type = event.type;
		Widget widget = event.widget;
		if (SWT.Selection == type) {
			if (widget == this.msrBtn) {
				FileDialog dialog = new FileDialog(shell, SWT.SIMPLE | SWT.OPEN
						| SWT.SINGLE);
				dialog.setFilterPath("E:\\workspace\\eclipsej2ee3.7\\XXX\\conf");
				dialog.setFilterExtensions(new String[] { "*.xml" });
				String filePath = dialog.open();
				if (filePath == null) {
					return;
				}
				if (!dialog.getFileName().startsWith("message.")) {
					MessageBox box = new MessageBox(shell, SWT.ICON_INFORMATION);
					box.setText("Tip");
					box.setMessage("选择的不是语言文件");
					box.open();
					return;
				}
				this.msrText.setText(filePath);
			} else if (widget == this.projectBtn) {
				DirectoryDialog dialog = new DirectoryDialog(shell, SWT.OPEN
						| SWT.SINGLE);
				dialog.setFilterPath("E:\\workspace\\eclipsej2ee3.7");
				dialog.setMessage("选择工程父目录,该目录下的所有项目中src下的源代码都会被搜索,不是工程的文件夹被忽略");
				String container = dialog.open();
				if (container == null) {
					return;
				}
				this.projectText.setText(container);
			} else if (widget == this.searchBtn) {
				String btnText = this.searchBtn.getText();
				if ("搜索".equals(btnText)) {
					String msr = msrText.getText().trim();
					String father = projectText.getText().trim();
					if (msr.length() == 0 || father.length() == 0) {
						return;
					}
					this.resultText.setText("");
					this.statusLabel.setText("");
					this.isStopped = false;
					workThread = new WorkThread(msr, father);
					workThread.start();
					this.searchBtn.setText("暂停");
				} else if ("暂停".equals(btnText)) {
					this.isSuspended = true;
					this.searchBtn.setText("继续");
				} else if ("继续".equals(btnText)) {
					synchronized(this.msrList){
						this.isSuspended = false;
						this.msrList.notifyAll();
					}
					this.searchBtn.setText("暂停");
				}

			}
		}
		if (SWT.Dispose == type) {
			this.isStopped = true;
			display.dispose();
			System.exit(0);
		}
	}

	private void checkFile(final File project, final File src,
			final String id) {
		
		if (this.isStopped) {
			return;
		}
		if(this.isSuspended){
			synchronized(this.msrList){
				try {
					this.msrList.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		if (src.isDirectory()) {
			File[] files = src.listFiles();
			for (final File f : files) {
				if (this.isUsed) {
					return;
				}
				// 跳过类似.svn这样的目录
				if (f.isHidden()) {
					continue;
				}
				this.checkFile(project, f, id);
			}
		} else {
			// 跳过非java文件
			if (!src.getName().endsWith(".java")) {
				return;
			}
			if (display.isDisposed()) {
				return;
			}
			display.asyncExec(new Runnable() {
				@Override
				public void run() {
					if (statusLabel.isDisposed()) {
						return;
					}
					statusLabel.setText("查找ID: "+id+" 分析文件: " + src.getPath());
				}
			});
			try {
				br = new BufferedReader(new FileReader(src));
				int index = -1;
				while ((line = br.readLine()) != null) {
					index++;
					// 注释跳过
					if (this.isComment(line)) {

					} else {
						if (line.indexOf("\"" + id + "\"") != -1) {
							this.isUsed = true;
							System.out.println(id + " occurs at project: "
									+ project.getName() + ", file: "
									+ src.getPath() + ", row number: " + index);
							return;
						}
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
				return;
			} finally {
				if (br != null) {
					try {
						br.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	private boolean isComment(String line) {
		this.commentMatcher = this.commentPattern.matcher(line);
		if (this.commentMatcher.matches()) {
			// 注释块的第一行
			if (line.trim().startsWith("/*")) {
				this.preComment = true;
			}
			// 注释块的最后一行
			if (line.trim().endsWith("*/")) {
				this.preComment = false;
			}
			return true;
		}
		// 如果在注释块中间
		if (this.preComment) {
			return true;
		}
		return false;
	}

	private void setBtnStatus(final boolean flag) {
		display.asyncExec(new Runnable() {
			@Override
			public void run() {
				if (msrBtn.isDisposed() || projectBtn.isDisposed()) {
					return;
				}
				msrBtn.setEnabled(flag);
				projectBtn.setEnabled(flag);
			}
		});
	}

	// ==========================
	class WorkThread extends Thread {
		private String xmlPath;
		private String container;

		public WorkThread(String xmlPath, String container) {
			this.xmlPath = xmlPath;
			this.container = container;
		}

		@Override
		public void run() {
			setBtnStatus(false);
			msrList = new LinkedList<String>();
			Document doc = null;
			try {
				doc = new SAXBuilder().build(this.xmlPath);
			} catch (JDOMException e1) {
				e1.printStackTrace();
				return;
			} catch (IOException e1) {
				e1.printStackTrace();
				return;
			}
			Element root = doc.getRootElement();
			List<Element> children = root.getChildren("msr");
			if (children == null || children.size() == 0) {
				return;
			}
			Iterator<Element> it = children.iterator();
			while (it.hasNext()) {
				msrList.add(it.next().getChildText("id"));
			}
			doc = null;
			final File[] projects = new File(this.container).listFiles();
			int msrIndex = 0;
			int projectIndex = 0;
			for (final String id : msrList) {
				if (isStopped) {
					return;
				}
				msrIndex++;
				isUsed = false;
				for (File f : projects) {
					if (isStopped) {
						return;
					}
					projectIndex++;
					final int ratio = (int) ((projects.length * (msrIndex - 1) + msrIndex) / (projects.length
							* msrList.size() / 100));
					if (display.isDisposed()) {
						return;
					}
					display.asyncExec(new Runnable() {
						@Override
						public void run() {
							if (progressBar.isDisposed()) {
								return;
							}
							progressBar.setSelection(ratio);
						}
					});
					// 非目录则跳过
					if (f.isFile()) {
						continue;
					}
					// 不是工程则直接跳过
					if (!new File(f, ".project").exists()) {
						continue;
					}
					// 隐藏目录跳过
					if (f.isHidden()) {
						continue;
					}
					File src = new File(f, "src");
					if (!src.exists()) {
						continue;
					}
					checkFile(f, src, id);
					if (isUsed) {
						break;
					} else {

					}
				}
				if (!isUsed && !isStopped) {
					if (display.isDisposed()) {
						return;
					}
					display.asyncExec(new Runnable() {
						@Override
						public void run() {
							if (resultText.isDisposed()) {
								return;
							}
							resultText.append(id
									+ System.getProperty("line.separator"));
						}
					});
				}
			}
			setBtnStatus(true);
			display.asyncExec(new Runnable() {
				@Override
				public void run() {
					if (statusLabel.isDisposed()) {
						return;
					}
					searchBtn.setText("搜索");
					statusLabel.setText("搜索完毕,共有"
							+ (resultText.getLineCount() - 1) + " 条记录");
				}
			});
		}
	}
}


这里遇到的几个问题:FileDialog和DirectoryDialog不能指定size和location, 如果想指定size和location,只能用Shell去模拟Dialog了。

还有两个常见的技术:在暂停搜索的时候,用了wait和notifyAll,由于搜索是个比较耗时的工作,因此不能直接在GUI线程中去完成,这里用WorkThread去完成搜索,由于swt和swing都是GUI 单线程机制,如果在GUI/EDT中去完成搜索的话,会造成UI假死。解决方法就是display.asyncExec和display.syncExec,SwintUtilities.invokeLater,SwingUtilities.invokeAndWait,前者表示直接让UI事件插队而返回,后者表示等待UI事件执行完才返回。

你可能感兴趣的:(SWT)