文本编辑器的开发
当今,计算机以及网络技术的飞速发展,社会正快速向信息化社会前进,我们需要更智能,更专业的软件帮助我们完成工作,从而提高了工作效率。
目前文本编辑器种类很多,所提供的功能也很多,但是能满足用户实现多种功能和进行Java的编译与运行很少,不能更好的适应当前用户的要求。本设计所完成的文本编辑器功能是针对学习Java程序语言,因此我们利用Java程序设计虚拟机和软件对用户及使用者的应用过程形成一整套完整的编写代码,编译,运行。
本文本编辑器的开发主要包括界面窗口的菜单栏和工具栏以及树结构,还有链接Java虚拟机实现编译与运行。使用SUN公司的Java中的GUI作为开发技术,利用其提供的各种类与接口,首先建立系统应用原型。
本文本编辑器的开发采用JDK和 UltraEdit为开发工具,操作系统为Windows98/Windows /Windows XP/win7等。
面向对象程序设计,GUI,文本编辑器,编译,运
目 录
1 前 言.................................................. 1
2 问题的提出及可行性研究.................................. 2
2.1 问题的提出及定义....................................... 2
2.2 可行性研究............................................. 3
2.2.1 对现行文本编辑器的分析............................... 3
2.2.2 新文本编辑器的可行性研究分析......................... 3
3 用户需求分析............................................ 5
3.1 用户调查............................................... 5
3.2 确定目标系统的功能..................................... 5
4 系统设计与开发环境...................................... 6
4.1系统相关类的设计....................................... 6
4.2 开发语言及其开发环境.................................. 10
4.2.1开发语言概述........................................ 10
4.2.2 Java开发环境——JDK的介绍.......................... 12
4.3 环境变量的设置........................................ 13
4.3.1.环境变量的作用...................................... 13
4.3.2 如何设置环境变量.................................... 14
5 关键技术和源代码的实现................................. 16
5.1 关键技术剖析.......................................... 16
5.2 源码分析.............................................. 17
6 系统测试............................................... 42
6.1 测试方案.............................................. 42
6.2 测试项目.............................................. 42
6.3 编辑器截图演示........................................ 43
1 前 言
Java可以方便的开发GUI程序,在Java语言提供的GUI构建工具中,可以分为“组件(component)”和“容器”(container)两种。组件有:按钮,标签,复选框,单选按钮,选择框,列表框,文本框,滚动条,画布,和菜单等;容器有:程序的启动封面,窗体和对话框。本设计主要是通过AWT(抽象窗口化工具包)和Swing来实现功能的。
本文本编辑器的设计,能打开,编辑和保存html,java,cpp,txt文件,能够在文件中进行查找和替换,而且支持java源文件的编译与运行,前提是机器上已经安装了java虚拟机并配置好了java相关的环境变量。
此文本编辑器是集代码编写,源代码编译和类运行于一体。
2 问题的提出及可行性研究
2.1 问题的提出及定义
随着计算机技术的不断发展,文本编辑器成为大家必不可少的工具,但是不同的用户对文本编辑器有不同的功能需求,所以除了像office中的word还有去WPS等大众文本编辑器,还需要一些带有一定相关专业功能的文本编辑器,可以方便用户进行工作与开发。
1、本设计就是针对java初学者所开发的,集源代码编辑,编译,运行于一体的文本编辑器。
2、根据此文本编辑器的的实际要求,本系统需要实现以下功能:
3、要求在运行该编辑器时,可以直接编辑默认的新建文件File,在左边的树中正在编辑的文件显示有File1;
4、要求在文本编辑器的右边的文件文本域用于编辑文字和代码,右边中间的文字域时显示光标的行号,右边下面的文本域是显示编译和运行信息;
5、要求在窗口顶部设有菜单栏,分别有File,Option,Advance,Edit,Help;
6、要求在菜单栏下面设有工具栏,分别有打开按钮,新建按钮,保存按钮,帮助按钮,推出按钮,编译按钮,运行按钮,复制按钮,剪切按钮,粘贴按钮;
7、要求能新建,打开和保存文件;若是新建文件则将新建文件保存为newFile1.txt,在编辑器左边树中正在编辑的文件显示为newFile1.txt,右边的文件文本域所在的Tab标签上显示的也是newFile1.txt。
8、要求能够通过查找功能,在当前文件中查找字符串,在弹出的对话框中输入所要查找的字符串,点击确定开始查找,找到后显示高亮;
9、要求能够连接本机的java虚拟机,对源代码进行编译及编译后运行
2.2 可行性研究
2.2.1 对现行文本编辑器的分析
市场上出现的和用户所使用的文本编辑器种类繁多,功能也是各式各样,其中有相当一部分是很优秀的。但是面向java初学者的操作简单的,界面友好的,集源代码编辑,编译,运行于一体的编辑器并不多见。
2.2.2 新文本编辑器的可行性研究分析
根据此文本编辑器的使用对象,新目标的提法不尽相同,例如:
1、提高工作效率和降低难度;
2、提供新的编译与运行处理功能;
新编辑器的目标确定后,就可以从以下三方面对能否实现新目标进行可行性分析:
(1)技术可行性
面向对象是一种新兴的程序设计方法,或者说是一种新的程序设计规范(paradigm),其基本思想是使用对象、类、继承、封装、消息等基本概念来进行程序设计。从现实世界中客观存在的事物(即对象)出发来构造软件系统,并且在系统构造中尽可能运用人类的自然思维方式。开发一个软件是为了解决某些问题,这些问题所涉及的业务范围称作该软件的问题域。其应用领域不仅仅是软件,还有计算机体系结构和人工智能等。许多程序开发工具,如 java、Visual Basic 6.0、Delphi等等,都支持面向对象的程序开发。
这些技术目前已经成熟,被广泛应用与各个领域。运用GUI中的awt和swing以及相关的类完全可以实现所要求的功能。因此,其设计开发在技术上是可行的。
(2)经济可行性
经济可行性研究是对设计投入使用后带来的经济效益进行估计。本设计所消耗的物力与财力不多,关键是思想架构,设计成功后可在互联网上供用户下载。经济效益与经济投资是很少的,也达到收支平衡。
(3)运行可行性
系统设计并运行于当前流行的Windows系列操作系统,具有Windows传统的操作风格,仅要求系统带有JVM(java虚拟机)。此文本编辑器操作简单,界面友好,用户可以立即使用。总之,系统具有较好的运行可行性。
综上所述,开发一个专门的java文本编辑器是可行的。
3 用户需求分析
3.1 用户调查
经过详细的调研,并多次与初学java潜在用户进行研讨后,首先加深了对java文本管理器的的深刻了解,其次,充分熟悉java源代码编辑,编译和运行。
3.2 确定目标系统的功能
通过详细的用户调查,可以基本确定目标系统要达到的目标了。
需求分析的任务是确定功能必须完成的工作,也就是对目标系统提出完整、准确、清晰、具体的要求。在经过多方了解和调查后,基本清楚了此文本编辑器的功能要求。
必须完成的功能如下:
- 文本新建功能;
- 文本打开功能;
- 文本保存功能;
- 字符串查询功能;
- 源文件编译功能;
(6) 代码运行功能。
4 系统设计与开发环境
系统设计一般分为总体设计和详细设计。经过需求分析阶段的工作,已经清楚系统必须完成的工作,下面的工作就应该是决定“如何做”的问题。总体设计的基本目的就是“概括地说系统应该如何实现?”。另一方面的主要工作是设计软件的结构,即确定系统都由哪些模块组成及模块之间的相互关系。
4.1系统相关类的设计
Editor Pack ( ); setLocatrion ( ); setVisible ( ); start ( ); setFocusable ( ); addKeyListrner ( ); |
Frame addImpl 添加指定的子 getContentPane 返回此窗体的 setLayout 设置 setJMenuBar 设置此窗体的菜单栏 setIconImage setDefaultCloseOperation remove 从该容器中移除指定组件 |
图4-1 Editor继承了JFrame类
JComponet |
JTextComponent |
JMenuBar |
JOptionPane |
JToolBar |
JTree |
JPanel |
JFileChooser |
JSplitpane |
AbstratButton |
JTabbedPane |
JScrollPane |
JButton |
JMenuItem |
JMenu |
JTextArea |
图4-2 是本设计中用到的JComponent及其扩展类的总括。
ActionListener actionPerformed 发生操作时调用 |
Act_StartFlash actionPerformed(ActionEvent E_start) |
Act_StopFlash actionPerformed(ActionEvent E_stop) |
Act_Timer actionPerformed(ActionEvent E_time) |
Act_Copy actionPerformed(ActionEvent e_ji9) |
Act_Paste actionPerformed(ActionEvent e_ji11) |
Act_Cut actionPerformed(ActionEvent e_ji10) |
图4-3 本图显示复制,粘贴,剪贴,动画的显示停止,启动,暂停事件继承ActionListener类
JFileChooser getFileView getIcon getName getSelectedFile setCurrentDirectory 设置当前目录 setDialogTitle 设置显示在 |
FileChooser Filechooser(); approveSelection(); cancelSelection(); |
图4-4 FileChoose类继承了JFileChoose类;此文件选择器能够打开和保存文件
4.2 开发语言及其开发环境
4.2.1开发语言概述
Sun公司在Java语言的白皮书中是这样定义Java的:
Java是一个简单的、面向对象的、分布的、健壮的、安全的、与平台无关的、可移植的、高性能的、多线程的,以及动态的解释型语言。
Java是一种简单的语言。它用到的概念不多,而且多为程序员所熟悉。如果你是一名程序员,掌握Java对你来说是易如反掌的事。即使你没有学过任何编程语言,学习Java也要比学习C++要容易的多。
由于Java最初是为控制电子产品设计的,因此它必须简单明了。为了保证这种简单性,Java去掉了C++中许多复杂的、冗余的、有二义性的概念,例如操作符重载、多继承、数据类型自动转换等。为了将程序员从复杂的内存管理的负担中解脱出来,同时也是为了减少错误,Java使用了自动内存垃圾收集机制,程序员只要在需要的时候申请即可,不需要释放,而由Java自己来收集、释放内存中的无用的块。
Java是一种支持分布式操作的程序设计语言。使用Java提供的URL类,用户可以象访问本地文件一样访问网络上的对象,使用非常方便。在客户机/服务器的模式下,Java还可以将运算从服务器端分散到客户端,提高系统的效率,避免了服务器的瓶颈制约。Java的网络类库支持分布式的编程。Socket类提供可靠的流式网络的连接,支持TCP/IP协议。通过编写协议句柄,程序员还可以扩充Java支持的协议集合。
Java提供非常有效的安全控制。由于Java应用于网络程序的开发,因而安全性变的至关重要。因为Java小程序需要下载到客户端解释执行,所以,如果没有安全控制,就会给一些网络黑客以可乘之机,这对用户来说是非常危险的。所幸的是,Java的安全机制可以有效的防止病毒程序的产生、下载程序对本地文件系统的破坏,以及网络黑客窃取密码和入侵。
Java是一种非常健壮的语言。因为在Java中使用了以下手段:
自动内存垃圾收集机制。Java自动收集无用的内存单元,进而防止了由于内存泄漏导致的动态内存分配问题。
完善的异常处理机制,既简化了错误处理任务和恢复,也增加了程序的可读性。
Java具有非常好的平台无关性和可移植性。因为Java最初是为对电子产品编程而设计的,所以它具有完美的平台无关性。它使用一种与平台无关的代码──字节码,而不是通常的特定机器上的机器码,由平台上的Java虚拟机中的Java解释器解释执行。Java虚拟机是免费的,在许多平台上都有。
Java提供了良好的可移植性。使用Java作为编程语言,只要进行一次程序开发工作,所开发的程序不需要经过任何改动,便能在各种平台上运行。Java使用两种方法使Java的应用程序不依赖与具体的系统:
采用基于国际标准的数据类型。Java的原始数据类型在任何机器上都是一样的,例如整型总是32位,长整型总是64位等。
Java是一种高性能的语言。“鱼与熊掌不可兼得”,通常,健壮性、安全性、平台无关性、可移植性等方面的提高总是要以牺牲性能为代价的。Java也不例外,Java的内存管理增加了运行时系统的复杂性,因为Java运行时系统必须内嵌一个内存管理模块;同样,Java程序的解释执行的效率也要低于直接执行编译后的源码的效率。但是Java采用了一些很好的措施来弥补这些性能上的差距:
生成高效的字节码。Java字节码的设计充分考虑了性能的因素,字节码的格式简单,解释器可以生成高效的机器码。
提供了即时编译和嵌入C代码的可选措施。即时编译是指在运行时把字节码编译成机器码。
4.2.2 Java开发环境——JDK的介绍
在Sun公司推出Java语言的同时,也推出了Java的一系列开发工具,如JDK(Java Developer’s Kit)。JDK是可以从网上免费下载的Java开发工具集。随后,其它一些著名的公司也相继推出了自己的Java开发工具,例如Microsoft公司的Visual J++,Borland公司的JBuilder,IBM公司的VisualAge for Java,Synmentac的Café等。本节中将简单的介绍一下Sun公司的JDK;至于Microsoft公司的Visual J++,将是本书其它章节重点介绍的内容。
JDK是Sun公司推出的Java开发工具集,由于Sun公司是Java语言的创始者和Java规范的制定者,所以JDK自然是最权威的Java开发工具。您写的Java程序是否符合Java语言规范的要求,用JDK提供的Java编译器一试便知。市场上其它公司的Java开发工具有的在某些地方并不完全遵从Java语言规范,例如Microsoft公司的Visual J++,但是JDK却绝对和Java语言规范保持一致,而且每当有新的Java语言规范推出时,就会有相应的JDK同时推出。当前JDK已经推出了其最新版本──JDK1.6
4.3 环境变量的设置
4.3.1.环境变量的作用
JAVA程序能够顺利编译需要两个环境变量,一个是PATH(path),一个是CLASSPATH(classpath)。那么这两个环境变量代表什么呢?或者说究竟在JAVA程序编译中有什么作用呢?这就要涉及到WINDOWS的基本知识了。在WINDOWS环境中,每一条能用的指令都保存在硬盘的“某个角落”,比如说指令NOTEPAD(WINDOWS的记事本指令,在C:\WINNT目录下),那么在DOS命令提示符下直接输入NOTEPAD就可以打开记事本了。但是一旦我们把C:\WINNT目录下的NOTEPAD.EXE这个可执行文件移开(比如说我们现在把它“剪切”到D盘根目录下)我们再在DOS提示符下直接输入NOTEPAD时就无法再打开记事本了,但是我们在DOS下改变路径输入D:\NOTEPAD(或者先输入cd D: 在输入NOTEPAD也行)就可以打开记事本了。这就是说在我们直接输入NOTEPAD的时候,其实其默认路径就是C:\WINN。假设我们要运行一个JAVA程序(hello.java)就要先把它的class文件移动到JAVA里的bin目录下 然后打开DOS,把路径改到JAVA的BIN目录下 执行java hello.class,一次无所谓 但是每次都要这样去做未免太麻烦。还好WINDOWS提供了一个PATH环境变量,他的作用就是现在这个变量的值路径中去找,如果找到了要运行的EXE就运行,否则失败。如果我们把JAVA里的bin目录这个路径赋给path那么下次我在运行java这个命令的时候,即便是直接输入DOS也能够在path变量中找到JAVA的bin目录下的java指令了。
“假设我们要运行一个JAVA程序(hello.java)就要先把它的class文件移动到JAVA里的bin目录下”,为什么要这样做呢?因为我们在用到java hello.class这条指令的时候,DOS不知道hello.class这个文件在哪里,他就先到当前目录中去找,如果找到就运行,否则失败。当然,如果说hello.java在D盘根目录下,你也可以用java D:\hello.class这条指令来运行,但是每次这样做也确实够麻烦。WINDOWS就提供了这样一个环境变量——classpath,如果没有就应该新建一个。我们把D:\这个路径赋值给classpath,那么下次在运行java hello.java的时候就先在当前目录中到,如果找到就运行,如果找不到就在classpath的值路径下去找,找到就运行,找不到就失败。
以上阐述了path和classpath两个环境变量的作用,如果设置好了。java的编译和运行就基本上没什么问题了。
4.3.2 如何设置环境变量
Windows下JAVA环境变量的设置祥解:
Windows下JAVA用到的环境变量主要有3个,JAVA_HOME、CLASSPATH、PATH。下面逐个分析。
JAVA_HOME指向的是JDK的安装路径,如x:\JDK_1.4.2,在这路径下你应该能够找到bin、lib等目录。值得一提的是,JDK的安装路径可以选择任意磁盘目录,不过建议你放的目录层次浅一点,如果你放的目录很深,比如x:\XXXXXX\xxxxx\XXXX\xxxx\XXXX\xxxx\XXXX\xxx……
设置方法如下:
JAVA_HOME=x:\JDK_1.4.2
PATH环境变量原来Windows里面就有,你只需修改一下,使他指向JDK的bin目录,这样你在控制台下面编译、执行程序时就不需要再键入一大串路径了。设置方法是保留原来的PATH的内容,并在其中加上%JAVA_HOME%\bin (注,如果你对DOS批处理不了解,你可能不明白%%引起来的内容是什么意思;其实这里是引用上一步设定好的环境变量JAVA_HOME,你写成x:\JDK_1.4.2也是可以的;你可以打开一个控制台窗口,输入echo %JAVA_HOME%来看一下你的设置结果) :
PATH=%JAVA_HOME%\bin;%PATH%
同样,%PATH%是引用以前你设置的PATH环境变量,你照抄以前的值就行了。
CLASSPATH环境变量我放在最后面,是因为以后你出现的莫名其妙的怪问题80%以上都可能是由于CLASSPATH设置不对引起的,所以要加倍小心才行。
CLASSPATH=.\;%JAVA_HOME%\lib\tools.jar。
首先要注意的是最前面的".\;",这个是告诉JDK,搜索CLASS时先查找当前目录的CLASS文件——为什么这样搞,这是由于LINUX的安全机制引起的,LINUX用户很明白,WINDOWS用户就很难理解(因为WINDOWS默认的搜索顺序是先搜索当前目录的,再搜索系统目录的,再搜索PATH环境变量设定的)。
为什么CLASSPATH后面指定了tools.jar这个具体文件?不指定行不行?这个是由java语言的import机制和jar机制决定的。
具体的设定方法: win2k\xp用户右键点击我的电脑->属性->高级->环境变量,修改下面系统变量那个框里的值就行了。
win9x用户修改autoexec.bat文件,在其末尾加入:
set JAVA_HOME=x:\JDK_1.4.2
set PATH=%JAVA_HOME%\bin;%PATH%
set CLASSPATH=.\;%JAVA_HOME%\lib\tools.jar
5 关键技术和源代码的实现
5.1 关键技术剖析
JFrame类的setJMenuBar的方法为窗口添加菜单栏JMenuBar,JMenuBar的add方法为菜单栏添加菜单JMenu,JMenu的add方法为菜单添加菜单项JMenuItem,它们之间的关系是树形关系。需要为每个菜单项添加事件处理器,当单击菜单项时,触发事件。
JToolBar为工具栏,可以在工具栏中添加按钮或者标签。JButton的setToolTipText方法设置按钮的提示信息,当鼠标停留在按钮上若干时间后,会显示提示信息。
分隔窗格JSplitPane类用于分隔两个组件,可以由用户交互的调整组件的大小。根据分隔条的方向,把JSplitPane分成有两种:VERTICAL_SPLIT表示水平分隔条,HORIZONTAL_SPLIT表示垂直分隔条。setDividerlocation方法设置分隔条的坐标。
JTree类实现树结构,树中有多个节点,但是且只有一个根节点。通用的节点类型为DefaultMutableTreeNode,它的add方法为节点添加子节点。
JTextArea文本域显示文件内容,将它放置在JScrollPane中,使其具备滚动条。setCareColor方法设置插入光标的颜色;addCareListener方法为文本域设置光标事件处理器,当光标位置改变时触发该类事件;getText和setText方法获得和设置文本域的内容;select方法高亮显示文本域中指定的文本;setCaretPositin方法获得光标在文本域中的位置;getLineOfOffset方法获得指定位置在文本域中的行号;getLineStartOffset方法获取指定行的第一个字符在文本域中的位置;getLineEndOffset方法获取指定行的最后一个字符在文本去中的位置。
JTabbedPane实现多页面的管理,每个页面有一个标签。addTab方法添加页面;addChangeListener方法设置页面切换事件处理器;setSelectedIndex方法设置选择页面编号;getSelectendIndex方法获取被选择的页面的编号;setTitleAt方法设置指定页面标签;getTitleAt方法获取指定页面的标签。
组件的addKeyListener方法为组件注册键盘事件管理器,键盘事件管理器必须实现KeyListener接口,或者继承KeyAdapter,keyPressed方法处理按键事件,参数类型为keyEvent,KeyEvent的getKeyCode方法可以获得备按下的键的代码;isControlDown方法判断Ctrl键是否被按下;isAltDown方法判断Alt键是否被按下。
JFileChoose文件选择器能够打开和保存文件,提交选择时approveSelection方法被调用,取消选择时cancelSelection方法被调用;getCurrentDirectory方法获得文件选择器当前目录,getSelectedFile方法获得文件选择器选择的文件。
JOptionPane用于弹出标准的对话框,类型有很多种。showConfirmDialog方法弹出确认信息对话框,要求用户确认或者取消;showMessageDialog方法弹出提示信息提示框;showInputDialog方法弹出输入信息对话框,要求用户输入信息。
在编译和运行Java程序时,需要调用本地的java和javac命令,通过Runtime的exec方法可以调用本地命令。
5.2 源码分析
(1) Editor类
/**
* 一个简单的文本编辑器,能够编辑html、java、cpp、txt文件,
* 并支持java的编译与运行
*/
public class Editor extends JFrame {
/********菜单栏、菜单和菜单项*********/
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
JMenu optionMenu = new JMenu("Option");
JMenu advanceMenu = new JMenu("Advance");
JMenu helpMenu = new JMenu("Help");
JMenu editMenu = new JMenu("Edit");
JMenuItem newMenuItem = new JMenuItem("new");
JMenuItem openMenuItem = new JMenuItem("Open");
JMenuItem saveMenuItem = new JMenuItem("Save");
JMenuItem exitMenuItem = new JMenuItem("exit");
JMenuItem findMenuItem = new JMenuItem("Find");
JMenuItem findNextMenuItem = new JMenuItem("Find Next");
JMenuItem replaceMenuItem = new JMenuItem("Replace");
JMenuItem compileMenuItem = new JMenuItem("Compile");
JMenuItem buildMenuItem = new JMenuItem("Build");
JMenuItem stopFlashMenuItem = new JMenuItem("Stop Flash");
JMenuItem startFlashMenuItem = new JMenuItem("Start Flash");
JMenuItem helpMenuItem = new JMenuItem("Help");
JMenuItem copyMenuItem = new JMenuItem("Copy");
JMenuItem cutMenuItem = new JMenuItem("Cut");
JMenuItem pasteMenuItem = new JMenuItem("Paste");
/********文件内容的显示****/
//用多个文本域存放多个文件内容,文本域放在JScrollPane里
//而JScrollPane放在JTabbedPane中,这就是一个多页面的布局
//多个文本域,每个文本域显示一个文件的内容
JTextArea[] fileTextAreas = new JTextArea[10];
//编译或者运行时的控制台信息
JTextArea consoleTextArea = new JTextArea();
JScrollPane[] fileScrollPanes = new JScrollPane[10];
JScrollPane consoleScrollPane;
//多个文本域放在不同tab里
JTabbedPane fileTabbedPane = new JTabbedPane();
/**********工具栏以及工具栏上的按钮**********/
JToolBar toolBar = new JToolBar();
JButton openButton = new JButton(new ImageIcon(loadImage("image/open.gif")));
JButton newButton = new JButton(new ImageIcon(loadImage("image/new.gif")));
JButton saveButton = new JButton(new ImageIcon(loadImage("image/save.gif")));
JButton helpButton = new JButton(new ImageIcon(loadImage("image/help.gif")));
JButton exitButton = new JButton(new ImageIcon(loadImage("image/close.gif")));
JButton compileButton = new JButton(new ImageIcon(loadImage("image/compile.gif")));
JButton buildButton = new JButton(new ImageIcon(loadImage("image/build.gif")));
JButton copyButton = new JButton(new ImageIcon(loadImage("image/copy.gif")));
JButton cutButton = new JButton(new ImageIcon(loadImage("image/cut.gif")));
JButton pasteButton = new JButton(new ImageIcon(loadImage("image/paste.gif")));
//该文本域显示当前光标在当前文本域中的行号
JTextArea showLineNoTextArea = new JTextArea();
//对话框窗体,程序中所有对话框都显示在该窗体中
JFrame dialogFrame = new JFrame();
/*****文件名以树型结构在左边显示*******/
JTree tree;
DefaultMutableTreeNode root;
DefaultMutableTreeNode[] nodes = new DefaultMutableTreeNode[10];
/*******组件之间的分隔栏******/
JSplitPane leftRightSplitPane;
JSplitPane lineNoConsoleSplitPane;
JSplitPane treeFlashSplitPane;
JSplitPane tabbedLineNoSplitPane;
/**********文件选择、存储相关********/
//文件过滤器
Filter fileFilter = new Filter();
//文件选择器
FileChooser fileChooser = new FileChooser();
// 文件读写控制,0表示文件选择器读文件,1文件选择器标示写文件
int fileChooser_control = 0;
FileWriter fileWriter;
// tabbedPane中tab页的当前数量
int tb = 1;
int find_control = 0;
//文本域的控制器,指向当前操作的文本域
int textAreas_control = 0;
//当前文本域中的文本
String currentTextInTextArea;
//标志文件是否为新建的,如果是新建的文件,为true
boolean[] newFileFlags = new boolean[10];
//存放打开文件所在的目录
String[] directory = new String[10];
/********查找替换相关****/
//正在查找的字符串及其长度
String findWord;
int fingWordLength;
//保存正在查找的字符串在文本域中的文本的位置
int findIndex;
//被替换的文本的长度
int replaceLength = 0;
/********用于显示Flash的控制器****/
JLabel flashLabel = new JLabel(new ImageIcon(loadImage("image/Juggler0.gif")));
Timer timer = new Timer(100, new Act_timer());
int timerControl = 0;
/********帮助相关****/
Font font = new Font("Courier", Font.TRUETYPE_FONT, 14);
JTextArea helpTextArea = new JTextArea();
JFrame helpFrame = new JFrame("Help");
//构造函数
public Editor() {
super("文本编辑器");
//为窗体添加键盘事件处理器
//下面这一行非常重要,表示窗体能够接受焦点。
//如果没有这一句,按键盘会无效。
this.setFocusable(true);
this.addKeyListener(new MyKeyListener());
//为窗体添加窗口事件处理器
this.addWindowListener(new WindowListener());
//初始化
init();
setLocation(200, 200);
setVisible(true);
pack();
//初始时启动动画
timer.start();
}
该类是编辑器的主类。
init方法初始化编辑器上的所有控件。首先初始化10个文本域,表示该编辑器能同时编辑10个文件,并构建10个默认的树节点DefaultMutableTreeNode对象。初始化界面左边的JTree,设置它的根节点为“正在编辑的文件”,使用DefaultMutableTreeNode的add方法为根节点添加一个子节点,表示编辑器初始状态时时新建一个文件。JTree的setEditable方法设置树不可编辑。
初始化控制台的文本域consoleTextArea,用于输出编译和运行Java的信息。将控制台文本域放置在一个JScrollPane内,使得他具有滚动条。
初始化显示当前光标所在行数的文本域showLineNoTextArea,当用户移动编辑器的光标时,在该文本域中显示光标行数会跟着发生变化。通过setEnabled方法使得它不可编辑。
初始化菜单,包括如下几个菜单:“File”,“Option”,“Advance”,“Help”,“Edit”。每个菜单下都设置多个菜单项。
初始化工具栏,包括如下几个工具项:“New”,“Open”,“Save”,“Exit”,“Help”,“Complie”,“Build”,“Copy”,“Cut”,“Paste”。通过JButton的setToolTipText方法为工具项设置提示信息,当用户鼠标停留在按钮上时,显示提示信息。
将各大组件用JSplitPane组织起来,使得组件之间的大小可以任意的调节。最后把这些组件都放在窗口中。
initHelp方法初始化帮助信息。以表格的形式显示编辑器的快捷键信息,使用了JTable类实现表格,表格的列名是一个字符串数组,表格的数据是一个二维数组。helpTextArea文本域显示使用编辑器全部功能需要做的配置。
exitEdior方法退出编辑器。退出前通过JOptionPane的showXonfirmDialog方法弹出“确认退出”的对话框,当用户选择JOptionPane.YES_OPTION时才真正退出。
loadImage方法加载图片。根据图片名,使用Class的getResource方法获得图片的URL,通过getContent方法打开URL,获得图片的输入流,通过组件的createImage方法,把输入流构造成一个Image对象。
(2) MyKeyListener类
* 键盘事件处理器
*/
public class MyKeyListener extends KeyAdapter {
// 覆盖父类的keyPressed方法,处理键被按下时的事件。
public void keyPressed(KeyEvent e) {
// 按F12打开文件
if (e.getKeyCode() == KeyEvent.VK_F12){
(new Act_OpenFile()).actionPerformed(null);
}
// 按Ctrl加S键保存文件
else if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_S){
(new Act_SaveFile()).actionPerformed(null);
}
// 按Alt键查找
else if (e.isAltDown()){
(new Act_Find()).actionPerformed(null);
}
// 按F3查找下一个
else if (e.getKeyCode() == KeyEvent.VK_F3){
(new Act_FindNext()).actionPerformed(null);
}
// 按F4替换
else if (e.getKeyCode() == KeyEvent.VK_F4){
(new Act_Replace()).actionPerformed(null);
}
// 按Ctrl加N新建文件
else if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_N){
(new Act_NewFile()).actionPerformed(null);
}
// 按Ctrl加E退出编辑器
else if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_E){
(new Act_ExitEditor()).actionPerformed(null);
}
// 按F5运行Java程序
else if (e.getKeyCode() == KeyEvent.VK_F5){
(new Act_Build()).actionPerformed(null);
}
// 按F9编译Java源代码
else if (e.getKeyCode() == KeyEvent.VK_F9){
(new Act_Compile()).actionPerformed(null);
}
// 按Ctrl加H显示帮助
else if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_H){
(new Act_Help()).actionPerformed(null);
}
}
}
/**
该类用于处理键盘事件,继承KeyAdpter。
覆盖了KeyAdapter的keyPressed方法,处理键盘被按下的事件,参数为KeyEvent类型。通过KeyEvent的getKeyCode方法获得被按下键盘的码,isControlDown方法判断Ctrl键是否被按下,isAltDown方法判断Alt键是否被按下。根据不同的按键组合,调用不同的方法去处理。
(3) FileChoose类
/**
* 文件选择器
*/
class FileChooser extends JFileChooser {
public FileChooser() {
//文件选择器默认位置为当前文件夹
super("./");
}
/**
* 提交选择
*/
public void approveSelection() {
String filename = null;
//fileChooser_control == 0表示现在是打开文件,需要读
if (fileChooser_control == 0) {
//打开文件时,在左边的树中添加一个节点
root.add(nodes[tb]);
remove(tree);
tree = new JTree(root);
tree.setBackground(new Color(70, 80, 91));
treeFlashSplitPane.setRightComponent(tree);
treeFlashSplitPane.setDividerLocation(120);
// 新建一个tab页,用于装新打开的文件
fileTabbedPane.addTab("File" + (tb + 1), fileScrollPanes[tb]);
fileTabbedPane.setSelectedIndex(tb);
//将当前文本域设置到新打开的文件上
textAreas_control = tb;
tb++;
//获取待打开的文件名
filename = fileChooser.getSelectedFile().getName();
//获取待打开的文件所在的目录,将目录保存至数组,这样在保存文件的时候,能够将文件名保存到目录中
directory[textAreas_control] = fileChooser.getCurrentDirectory().toString();
fileTextAreas[textAreas_control].setText(null);
try {
//将文件内容显示到文本域中
String str;
BufferedReader breader = new BufferedReader(new FileReader(
directory[textAreas_control] + "/" + filename));
while (true) {
str = breader.readLine();
if (str == null) {
break;
}
fileTextAreas[textAreas_control].append(str + '\n');
}
} catch (Exception e_open) {
JOptionPane
.showMessageDialog(dialogFrame.getContentPane(), "读取发生错误");
}
} else if (fileChooser_control == 1) {
// fileChooser_control == 1表示现在是保存新文件,需要写
filename = fileChooser.getSelectedFile().getName();
directory[textAreas_control] = fileChooser.getCurrentDirectory().toString();
try {
//将文本域中的内容写到文件中
fileWriter = new FileWriter(directory[textAreas_control] + "/"
+ filename);
fileWriter.write(fileTextAreas[textAreas_control].getText());
fileWriter.close();
} catch (Exception e_save) {
JOptionPane
.showMessageDialog(dialogFrame.getContentPane(), "读取发生错误");
}
}
//关闭对话框
dialogFrame.dispose();
//更新文件目录树中的名字
root.remove(nodes[textAreas_control]);
nodes[textAreas_control] = new DefaultMutableTreeNode(filename);
root.add(nodes[textAreas_control]);
//将当前显示的树删除、显示新的树
remove(tree);
tree = new JTree(root);
tree.setBackground(new Color(70, 80, 91));
treeFlashSplitPane.setRightComponent(tree);
treeFlashSplitPane.setDividerLocation(120);
//将tab页的标题改为文件名
fileTabbedPane.setTitleAt(textAreas_control, filename);
//无论是打开、还是保存,这个文件不是已经新建的,所以置为false
newFileFlags[textAreas_control] = false;
}
/**
* 取消选择
*/
public void cancelSelection() {
dialogFrame.dispose();
}
}
自定义的文件选择器类,继承了JFileChooser。
approveSelection方法实现文件的选择。当用户通过文件选择器文件时,单击“确定”按钮时被调用。fileChooser_control变量值为0表示当前的文件选择器是为了打开文件,变量值为1时表示为了保存文件。
打开文件时,JFileChooser的getSelectedFile方法获得被选中的文件,getCurrentDirectory方法获得被选择文件所在的目录。从文本域数组fileTextAreas取出一个新元素,用于显示文件内容;同时将JTree的跟节点添加一个新页,把显示新闻家内揉的文本域组织在JTabbedPane中,并通过SetSelectedIndex方法将新页设为当前页。通过BufferedReader读取文件内容,并通过JTextArea的append方法把内容添加到文本域中。
保存文件时,使用FileWriter的write方法将文件的文本域中的内容保存到文件。
cancelSelection方法处理关闭文件选择器事件。当用户取消文件选择时,通过JFrame的dispose方法将文件选择器窗口关闭。
(4) Filter类
/**
* 文件过滤器,只支持编辑".java,*.html,*.txt,*.cpp"文件
*/
class Filter extends FileFilter {
// 覆盖FileFilter的accept方法
public boolean accept(File file1) {
return (file1.getName().endsWith(".java") || file1.isDirectory()
|| file1.getName().endsWith(".html")
|| file1.getName().endsWith(".txt") || file1.getName()
.endsWith(".cpp"));
}
public String getDescription() {
return (".java,*.html,*.txt,*.cpp");
}
}
该类实现了一个自定义的文件过滤器类,继承了FileFilter。
accept方法决定是否允许通过文件过滤器。允许如下文件被选择:“.java”,“.html”,“.txt”,“.cpp”。
getDescription方法获得文件选择器的描述,即文件选择框的文件类型描述。
(5) Carelis_lineNo类
/**
* 用于侦听文本组件插入符的位置更改的侦听器
* 获取当前光标在文件中的行号
*/
class CaretLis_lineNo implements CaretListener {
public void caretUpdate(CaretEvent e) {
try {
showLineNoTextArea.setText(" Cursor at the "
+ (fileTextAreas[textAreas_control]
.getLineOfOffset(fileTextAreas[textAreas_control]
.getCaretPosition()) + 1)
+ " line in the file of "
+ fileTabbedPane.getTitleAt(textAreas_control));
} catch (BadLocationException eB) {
System.out.println("IO Wrong");
}
}
}
该类实现了一个自定义的文本组件插入点改变事件处理器,实现了CaretListener接口。
实现了CareTListener接口定义的caretUpdate方法,当文本组件的插入点改变时,调用该方法。JTextArea的getCaretPosition方法获得文本域中光标插入点的位置,根据该位置,调用getLineOfOffset方法,能够获得插入点所在的行号。
(6) MouseListener_console类
/**
* 控制台文本域中鼠标事件的侦听器
* 当控制台中有错误信息时,表明Java源文件编译有问题,定位到出错的行号。
*/
class MouseListener_console extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
// 获取光标在控制台文本域中的位置
int off_err = consoleTextArea.getCaretPosition();
int line_no = 0;
try {
// 根据光标位置,定位它在控制台文本域中的行
int index = -1;
int line_err = consoleTextArea.getLineOfOffset(off_err - 1);
// 获取光标所在行的起始字符在文本域中的位置
int start_err = consoleTextArea.getLineStartOffset(line_err);
// 获取光标所在行的起始位置到光标位置之间文本,即错误信息
String err_str = consoleTextArea.getText(start_err, off_err - start_err);
// 高亮显示错误信息
consoleTextArea.select(start_err, off_err);
// 分析错误信息中是否有编译器指出的Java源文件行号,这里假定最多10000行源程序
for (line_no = 0; line_no < 10000; line_no++) {
index = err_str.indexOf(":" + line_no + ":");
if (index > 0) {
break;
}
}
// 如果能够分析出Java源文件的行号,则在文件文本域中高亮显示。
if (index != -1) {
fileTextAreas[textAreas_control].requestFocus();
fileTextAreas[textAreas_control].select(fileTextAreas[textAreas_control]
.getLineStartOffset(line_no - 1), fileTextAreas[textAreas_control]
.getLineEndOffset(line_no - 1));
}
} catch (BadLocationException eB) {
System.out.println("IO Wrong");
}
}
}
该类实现了一个自定义的鼠标事件处理器,继承了MouseAdapter。
覆盖了MoueAdapter的mouseClicked方法,当鼠标在控制台文本域中被单击时调用该方法。通过JTextArea的getCaretPosition方法获得插入点的位置,通过getLineOfOffset方法获得插入点所在的行号,getLineStarOffset方法获得行的起始字符在文本域中的位置,用getText方法提取行的起始位置到插入点间的字符串,并通过select方法将字符串高亮显示,这个字符串就是编译或者运行Java程序时控制台输出的一条错误信息。
分析错误信息,从错误信息中提取数字,如果有数字,则把数字当作是Java程序出错的行数,调用Java程序所在的文本域的requestFocus方法,使它获得焦点,根据出错的行号,使用JTextArea的select方法将出错行的文本高亮显示。
(7) Act_ChangeTab类
/**
* 切换tab页事件
*/
class Act_ChangeTab implements ChangeListener {
public void stateChanged(ChangeEvent e) {
// 切换tab页时,更新textAreas_control的值。
textAreas_control = fileTabbedPane.getSelectedIndex();
}
}
该类实现了一个自定义的改变事件处理器,实现了ChangeListener接口。
当切换JTabbedPane的页时出发该类事件,调用ChangeListener接口定义的stateChanged方法,在该方法中,通过JTabbedPane的getSelectedIndex方法获得被选择页的索引,并将它设置为当前操作页的索引。
(8) Act_Find类
/**
* 查找事件
*/
class Act_Find implements ActionListener {
public void actionPerformed(ActionEvent e_ji4) {
//查找对话框
findWord = JOptionPane
.showInputDialog("请输入查找内容");
if (findWord == null) {
JOptionPane.showMessageDialog(null, "查找失败!");
} else {
//根据查找内容在当前文本域中进行匹配
fingWordLength = findWord.length();
currentTextInTextArea = fileTextAreas[textAreas_control].getText();
findIndex = currentTextInTextArea.indexOf(findWord);
if (findIndex < 0){
JOptionPane.showMessageDialog(null,
" 查找内容不存在 ");
} else {
//如果找到了,则将鼠标键盘焦点放在当前文本域中,并将匹配字符串标示出来
fileTextAreas[textAreas_control].requestFocus();
fileTextAreas[textAreas_control].select(findIndex, findIndex + fingWordLength);
}
}
}
}
该类实现查找功能。实现了ActionListener接口,当用户选择“Find”菜单时,调用它的actionPerformed方法。
首先通过JOptionPane的showInputDialog方法显示一个输入对话框,要求用户输入查找内容,将查找内容返回。JTextArea的getText方法获得文本域中的文本,通过String的indexof方法判断文本是否包含查找内容,如果包含,则查找成功,通过select方法将匹配的内容高亮显示;如果不包含查找内容,则通过JOprionPane的showMessageDialog方法先死一个提示对话框,告知用户“查找的内容不存在”。
(9) Act_Replece类
/**
* 替换事件
*/
class Act_Replace implements ActionListener {
public void actionPerformed(ActionEvent e_ji5) {
//替换对话框
Object[] endButton1 = { "Replace", "Cancel" };
String message1 = "确认替换?";
currentTextInTextArea = fileTextAreas[textAreas_control].getText();
//获取被替换的内容
String seek_word = JOptionPane
.showInputDialog("请输入查找内容");
//获取替换后的内容
String replace_word = JOptionPane
.showInputDialog("请输入替换内容");
//如果用户输入的查找内容不为null,则开始进行替换操作
if (seek_word != null) {
//获取查找内容的长度,也就是将来替换的长度
replaceLength = seek_word.length();
while (true) {
//先获取当前文本域的文本,再进行查找
currentTextInTextArea = fileTextAreas[textAreas_control].getText();
findIndex = currentTextInTextArea.indexOf(seek_word, findIndex + replaceLength);
if (findIndex < 0) {
//文本中不存在查找内容
JOptionPane.showMessageDialog(null,
"查找已经到达文件尾!");
break;
} else {
//查找成功,则标示出查找内容
fileTextAreas[textAreas_control].requestFocus();
fileTextAreas[textAreas_control].select(findIndex, findIndex + replaceLength);
//替换确认
JOptionPane end1 = new JOptionPane(message1,
JOptionPane.WARNING_MESSAGE,
JOptionPane.DEFAULT_OPTION, null, endButton1);
JDialog endD1 = end1.createDialog(end1, "请选择");
endD1.setVisible(true);
Object push1 = end1.getValue();
if (push1 == endButton1[0]){
//如果用户选择替换,则将文本域中被标示的文字用replace替换
fileTextAreas[textAreas_control].replaceSelection(replace_word);
}
}
}
}
}
}
该类实现全部替换功能。实现了ActionListener接口,当用户选择“Replace”菜单时,调用它的actionPerformed方法。
首先通过JOptionPane的showInputDialog方法显示来能够个输入对话框,要求用户输入查找内容和替换内容。首先在文本域中进行查找,如果找到,通过select方法将匹配内容高亮显示,并请求用户确认替换。如果用户同意替换,调用JTextArea的replaceSelection方法替换文本域中被选选择的内容。
(10) Act_Compile类
/**
* 编译compile事件
*/
class Act_Compile implements ActionListener {
public void actionPerformed(ActionEvent e_ji6) {
//compile一个java源文件
//控制台信息
consoleTextArea.setText(null);
//目前只支持编译java类
if ((fileTabbedPane.getTitleAt(textAreas_control)).indexOf(".java") > -1) {
try {
int count;
byte input[] = new byte[256];
String InputString;
// 编译java源文件的命令"javac"
String[] command = {
"javac",
directory[textAreas_control] + "/"
+ fileTabbedPane.getTitleAt(textAreas_control) };
//当前应用程序启动一个新进程,用于执行命令
Process p = Runtime.getRuntime().exec(command);
//读取执行命令时的错误输出
BufferedInputStream bufin = new BufferedInputStream(p.getErrorStream());
//这里仅仅读取前1024个字节
bufin.mark(1024);
count = bufin.read(input);
if (count <= 0){
//编译没有错误
consoleTextArea.append("Compile to " + fileTabbedPane.getTitleAt(textAreas_control)
+ " Success");
} else {
//编译失败
InputString = new String(input, 0, count);
consoleTextArea.append("Compile to " + fileTabbedPane.getTitleAt(textAreas_control)
+ " Fail\n" + InputString);
}
} catch (IOException e) {
System.err.println("IO error: " + e);
}
} else {
consoleTextArea.append(fileTabbedPane.getTitleAt(textAreas_control)
+ " is not a java File !\n Please Check it again!");
}
}
}
该类实现了编译Java程序的功能。实现了ActionListener接口,当用户选择“Compile”菜单时,调用它的actionPerformed方法。
首先通过fileTabbdePane的getTitleAt方法获得当前被选择页的标题,即当前编辑的文件名,如果文件名已“.java”结尾,则进行编译。构造执行编译Java程序的javac命令,然后通过Runtime的getRuntime静态方法获得当前程序的运行时环境,Runtime的exec方法执行命令,返回一个进程Process对象,Process的getErrorStream方法获得进程的错误输出流,如果没有错误信息,则将控制台文本域consoleTextArea的内容设置为编译成功的消息,如果有错误信息,将错误信息显示在consoleTextArea中。
(11) Act_Build类
/**
* 运行build事件
*/
class Act_Build implements ActionListener {
public void actionPerformed(ActionEvent e_ji7) {
//build一个Java类
consoleTextArea.setText(null);
if ((fileTabbedPane.getTitleAt(textAreas_control)).indexOf(".java") > -1) {
try {
int count;
byte input[] = new byte[256];
String InputString;
//获取java类的名字,去掉java源文件名的后五个字母".java"
String class_name;
int length = (fileTabbedPane.getTitleAt(textAreas_control)).length();
class_name = (fileTabbedPane.getTitleAt(textAreas_control)).substring(0,
length - 5);
//运行java类的命令"java"
String[] command = { "java", "-classpath",
directory[textAreas_control], class_name };
Process p = Runtime.getRuntime().exec(command);
BufferedInputStream bufin = new BufferedInputStream(p
.getErrorStream());
bufin.mark(256);
count = bufin.read(input);
if (count <= 0){
//运行没有错误
consoleTextArea.append("Build to " + fileTabbedPane.getTitleAt(textAreas_control)
+ " Success");
} else {
//运行有错误
InputString = new String(input, 0, count);
consoleTextArea.append("Builld to " + fileTabbedPane.getTitleAt(textAreas_control)
+ " Fail\n" + InputString);
}
} catch (IOException e) {
System.err.println("IO error: " + e);
} catch (IndexOutOfBoundsException e2) {
System.err.println("IO error: " + e2);
}
} else {
consoleTextArea.append(fileTabbedPane.getTitleAt(textAreas_control)
+ " is not a java File !\n Please Check it again!");
}
}
}
与Act_Compile一样,只允许运行Java文件。构建运行Java程序的java命令,通过Runtime的exec方法执行java命令,返回一个Process对象,调用Proxess的getErrorStream方法获得进程的错误输出流,如果运行没有错误输出,则在consoleTextArea中显示运行成功地消息,如果有错误信息,则将错误信息显示在xonsoleTextArea中。
6 系统测试
6.1 测试方案
将程序类文件转化成EXE文件,打开后在编辑器中输入一段源代码,进行查找,保存,编译,执行。
6.2 测试项目
该测试计划主要包括对程序各个功能的测试,有:
1、文本新建功能;
2、文本打开功能;
3、文本保存功能;
4、字符串查询功能;
5、源文件编译功能。
6.3 编辑器截图演示
图6-1 为文本编辑器主界面。
参考文献
[1] Thingking in Java第三版 Bruce Eckel著 机械工业出版社
[2] Exploring Java, 2nd Edition Oreilly 著
[3] JAVA 2 核心技术卷2:高级特性 Cay S.Horstmann Gary Cornell
[4] Java编程实用技术与案例 译作者:杨绍方
[5] JavaJDK实例宝典 夏先波编著 电子工业出版社
[6] Java2编程详解(special edition java2)
[7] Java2核心技术卷一,二(core java2 volume1,2)
[8] Java XML编程指南 电子工业出版社 [美]Tom Myers,
[9] Jini核心技术 作者: W.Keith Edwards
[10] Enterprise JAVABEANS 作者:(美)Richard Monson-Haefel
[11] 数据结构与算法分析(Java版) [美]Clifford A.Shaffer
[12] 软件工程Java语言实现 作者: Stephen R.Schach
[13] java参考大全 作者:Herbert Schildt
[14] Java语言程序设计 [美]Y.Daniel Liang 著 王镁 李娜 译
机械工业出版社
[15] Java语言规范 [美]James Gosling