StyledText实现的简单Java类文件编辑器

        因为所处行业缘故,实施拷贝的虚拟机中都是只有环境,没有源码,包括Java的和TC的,所有的东西都没有源码,只能自己装反编译插件查看源码,但我不知道为啥,这个反编译插件反编译出来的代码前面都有空注释,中间穿插大量的空白行,最后还有反编译信息。反编译信息倒无所谓,只是代码开头的空注释和中间的空白行很让人眼花,很讨厌。某天心血来潮,自己写一个编辑器,处理一下这些代码。于是开动了。

        其实这个东西写完后自己发现,基本毫无用处,真正开发过程中看源代码时需要了解各个方法相互怎么调用的,还会通过代码大纲总体预览这个类什么的。而自己实现的这个编辑器,除了看起来稍微好看一点,基本没啥用处。不过没关系,就当学习练手了吧。

       一开始就是想直接用SWT的Text行了,后来想起StyleText,看名字感觉更高端一些,应该能实现很多炫酷的功能,就决定用StyleText了,然后深入研究发现,这两个玩意根本不是一回事。先看下继承关系。

                                                 

       

  是的,Text是Scrollable的直接子类,而StyledText是Canvas的直接子类,这意味着什么,整个StyledText就是用Canvas画出来的,感觉也是很牛逼啊。想想也是,不然Eclipse中的代码着色等等高级功能如何实现?单靠Text是无法完成的,功能太单一了。

       用上StyledText先实现我最基本的功能吧,就是简单的找到字符串中的前8列字符,中间的空白字符,和最后7行代码。把他们清除掉。实现完成后在网上搜索StyledText,发现还可以实现更多有意思的功能,内容助理,代码着色等,参考别人博客的代码和自己看源码,慢慢实现出了这几个功能,下面贴出效果图。

A.  这是从反编译器中粘贴过来的代码,很乱。

B. 经过解析后,去除了注释和空白行,并对关键字进行着色。

C.再次输入代码时,对相关内容会弹出内容助理的窗口

 D.如果输入的单词是关键字,会进行着色。

 

            好了,运行效果就是这样,下面贴出代码,相关的东西都已经在代码里详细注释了。

 

主类:

  1 import java.io.File;
  2 import java.io.FileWriter;
  3 import java.io.IOException;
  4 import java.util.ArrayList;
  5 import java.util.List;
  6 import java.util.regex.Matcher;
  7 import java.util.regex.Pattern;
  8 
  9 import org.eclipse.jface.dialogs.MessageDialog;
 10 import org.eclipse.jface.text.Document;
 11 import org.eclipse.jface.text.IDocument;
 12 import org.eclipse.jface.text.ITextSelection;
 13 import org.eclipse.jface.text.contentassist.ContentAssistant;
 14 import org.eclipse.jface.text.contentassist.IContentAssistant;
 15 import org.eclipse.jface.text.source.ISourceViewer;
 16 import org.eclipse.jface.text.source.SourceViewer;
 17 import org.eclipse.jface.text.source.SourceViewerConfiguration;
 18 import org.eclipse.swt.SWT;
 19 import org.eclipse.swt.custom.StyleRange;
 20 import org.eclipse.swt.custom.StyledText;
 21 import org.eclipse.swt.custom.VerifyKeyListener;
 22 import org.eclipse.swt.events.ModifyEvent;
 23 import org.eclipse.swt.events.ModifyListener;
 24 import org.eclipse.swt.events.SelectionAdapter;
 25 import org.eclipse.swt.events.SelectionEvent;
 26 import org.eclipse.swt.events.VerifyEvent;
 27 import org.eclipse.swt.layout.FillLayout;
 28 import org.eclipse.swt.widgets.Button;
 29 import org.eclipse.swt.widgets.Composite;
 30 import org.eclipse.swt.widgets.Control;
 31 import org.eclipse.swt.widgets.Display;
 32 import org.eclipse.swt.widgets.FileDialog;
 33 import org.eclipse.swt.widgets.Shell;
 34 import org.eclipse.wb.swt.SWTResourceManager;
 35 
 36 import swing2swt.layout.BorderLayout;
 37 
 38 public class ClassEditor {
 39 
 40     protected Shell shell;
 41     private SourceViewer sourceViewer;
 42     private List<String> keywords = new ArrayList<String>();
 43     private StyledText styledText;
 44     //匹配Java关键字的正则表达式
 45     public static final String REG= "\\b(abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|false|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|strictfp|super|switch|synchronized|this|throw|throws|true|transient|try|void|volatile|while)\\b";
 46     
 47     public static void main(String[] args) {
 48         try {
 49             ClassEditor window = new ClassEditor();
 50             window.open();
 51         } catch (Exception e) {
 52             e.printStackTrace();
 53         }
 54     }
 55 
 56     /**
 57      * Open the window.
 58      */
 59     public void open() {
 60         Display display = Display.getDefault();
 61         createContents();
 62         shell.open();
 63         shell.layout();
 64         while (!shell.isDisposed()) {
 65             if (!display.readAndDispatch()) {
 66                 display.sleep();
 67             }
 68         }
 69     }
 70 
 71     /**
 72      * Create contents of the window.
 73      */
 74     protected void createContents() {
 75         shell = new Shell();
 76         shell.setSize(612, 468);
 77         shell.setText("类文件编辑器");
 78         shell.setLayout(new BorderLayout(0, 0));
 79         
 80         sourceViewer = new SourceViewer(shell, null, SWT.V_SCROLL);
 81         //此处的Document非必备参数,但是当需要实现内容助手等复杂功能时,必须添加此参数,否则会一直报空指针异常
 82         //本人就是一直报异常,查看源码才找出这个问题的。内容助手底层代码使用到了这个Document对象。
 83         sourceViewer.setDocument(new Document());
 84         styledText = sourceViewer.getTextWidget();
 85         styledText.setText("/*     */ package org.eclipse.jface.dialogs;\r\n/*     */ \r\n/*     */ import org.eclipse.swt.custom.StackLayout;\r\n/*     */ import org.eclipse.swt.widgets.Composite;\r\n/*     */ import org.eclipse.swt.widgets.ProgressBar;\r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ public class ProgressIndicator\r\n/*     */   extends Composite\r\n/*     */ {\r\n/*     */   private static final int PROGRESS_MAX = 1000;\r\n/*  30 */   private boolean animated = true;\r\n/*     */   \r\n/*     */ \r\n/*     */   private StackLayout layout;\r\n/*     */   \r\n/*     */ \r\n/*     */   private ProgressBar determinateProgressBar;\r\n/*     */   \r\n/*     */ \r\n/*     */   private ProgressBar indeterminateProgressBar;\r\n/*     */   \r\n/*     */ \r\n/*     */   private double totalWork;\r\n/*     */   \r\n/*     */   private double sumWorked;\r\n/*     */   \r\n/*     */ \r\n/*     */   public ProgressIndicator(Composite parent)\r\n/*     */   {\r\n/*  49 */     this(parent, 0);\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */   public ProgressIndicator(Composite parent, int style)\r\n/*     */   {\r\n/*  62 */     super(parent, 0);\r\n/*     */     \r\n/*     */ \r\n/*  65 */     if ((style & 0x200) == 0) {\r\n/*  66 */       style |= 0x100;\r\n/*     */     }\r\n/*  68 */     this.determinateProgressBar = new ProgressBar(this, style);\r\n/*  69 */     this.indeterminateProgressBar = new ProgressBar(this, style | \r\n/*  70 */       0x2);\r\n/*  71 */     this.layout = new StackLayout();\r\n/*  72 */     setLayout(this.layout);\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */   public void beginAnimatedTask()\r\n/*     */   {\r\n/*  79 */     done();\r\n/*  80 */     this.layout.topControl = this.indeterminateProgressBar;\r\n/*  81 */     layout();\r\n/*  82 */     this.animated = true;\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */   public void beginTask(int max)\r\n/*     */   {\r\n/*  92 */     done();\r\n/*  93 */     this.totalWork = max;\r\n/*  94 */     this.sumWorked = 0.0D;\r\n/*  95 */     this.determinateProgressBar.setMinimum(0);\r\n/*  96 */     this.determinateProgressBar.setMaximum(1000);\r\n/*  97 */     this.determinateProgressBar.setSelection(0);\r\n/*  98 */     this.layout.topControl = this.determinateProgressBar;\r\n/*  99 */     layout();\r\n/* 100 */     this.animated = false;\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */   public void done()\r\n/*     */   {\r\n/* 107 */     if (!this.animated) {\r\n/* 108 */       this.determinateProgressBar.setMinimum(0);\r\n/* 109 */       this.determinateProgressBar.setMaximum(0);\r\n/* 110 */       this.determinateProgressBar.setSelection(0);\r\n/*     */     }\r\n/* 112 */     this.layout.topControl = null;\r\n/* 113 */     layout();\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */   public void sendRemainingWork()\r\n/*     */   {\r\n/* 120 */     worked(this.totalWork - this.sumWorked);\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */   public void worked(double work)\r\n/*     */   {\r\n/* 128 */     if ((work == 0.0D) || (this.animated)) {\r\n/* 129 */       return;\r\n/*     */     }\r\n/* 131 */     this.sumWorked += work;\r\n/* 132 */     if (this.sumWorked > this.totalWork) {\r\n/* 133 */       this.sumWorked = this.totalWork;\r\n/*     */     }\r\n/* 135 */     if (this.sumWorked < 0.0D) {\r\n/* 136 */       this.sumWorked = 0.0D;\r\n/*     */     }\r\n/* 138 */     int value = (int)(this.sumWorked / this.totalWork * 1000.0D);\r\n/* 139 */     if (this.determinateProgressBar.getSelection() < value) {\r\n/* 140 */       this.determinateProgressBar.setSelection(value);\r\n/*     */     }\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */   public void showError()\r\n/*     */   {\r\n/* 149 */     this.determinateProgressBar.setState(1);\r\n/* 150 */     this.indeterminateProgressBar.setState(1);\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */   public void showPaused()\r\n/*     */   {\r\n/* 158 */     this.determinateProgressBar.setState(4);\r\n/* 159 */     this.indeterminateProgressBar.setState(4);\r\n/*     */   }\r\n/*     */   \r\n/*     */ \r\n/*     */ \r\n/*     */ \r\n/*     */   public void showNormal()\r\n/*     */   {\r\n/* 167 */     this.determinateProgressBar.setState(0);\r\n/* 168 */     this.indeterminateProgressBar.setState(0);\r\n/*     */   }\r\n/*     */ }\r\n\r\n/* Location:           D:\\Siemens\\Teamcenter8\\portal\\plugins\\org.eclipse.jface_3.5.0.I20090525-2000.jar\r\n * Qualified Name:     org.eclipse.jface.dialogs.ProgressIndicator\r\n * Java Class Version: 1.2 (46.0)\r\n * JD-Core Version:    0.7.0.1\r\n */");
 86         styledText.setFont(SWTResourceManager.getFont("微软雅黑", 12, SWT.NORMAL));
 87         styledText.setAlwaysShowScrollBars(false);
 88         styledText.setSelectionBackground(SWTResourceManager.getColor(SWT.COLOR_BLACK));
 89         styledText.setBackground(SWTResourceManager.getColor(199, 237, 204));
 90         /**
 91          * 1.文本修改监听,只有当文本框内的内容被修改时执行的操作,添加或删除文字时会执行的操作
 92          *   其余的比如按下Shift键等操作不会被监听到,此监听应该和下面的那个键盘监听区分开来
 93          * 2.这个监听中实现了输入Java关键字后自动着色功能
 94          * 3.实现思路
 95          *   a,从SourceViewer对象中获取ITextSelection对象(所有Viewer中基本都有此对象)
 96          *   b,获取当前光标所处的位置,然后根据此位置获取当前输入的单词的起始位置
 97          *   c,计算当前输入的单词的长度并判断此单词是否是Java关键字,是就进行着色。
 98          */
 99         styledText.addModifyListener(new ModifyListener() {
100             public void modifyText(ModifyEvent e) {
101                 ITextSelection selection = (ITextSelection) sourceViewer.getSelectionProvider().getSelection();
102                 int offset = selection.getOffset();
103                 String key = MyContentAssist.extractPrefix(sourceViewer,offset);
104                 int p = offset-key.length();
105                 if(key.length()>0&&keywords.contains(key)) {
106                     styledText.setStyleRange(new StyleRange(p, key.length(), SWTResourceManager.getColor(SWT.COLOR_DARK_MAGENTA), null));
107                 }else {
108                     styledText.setStyleRange(new StyleRange(p, key.length(), null, null));
109                 }
110             }
111         });
112         
113         
114         Composite composite = new Composite(shell, SWT.NONE);
115         composite.setLayoutData(BorderLayout.SOUTH);
116         composite.setLayout(new FillLayout(SWT.HORIZONTAL));
117         
118         Button btnParse = new Button(composite, SWT.NONE);
119         btnParse.setText("解析");
120         btnParse.addSelectionListener(new SelectionAdapter() {
121             @Override
122             public void widgetSelected(SelectionEvent e) {
123                 /**
124                  * 功能:删除所有的注释和空白行
125                  * 思路:用换行符将整个文本分成数组,然后将每一行的前8个去掉,并将最后7行删除,最后拼接起来
126                  * 缺点:太死板
127                  */
128                 String string = styledText.getText();
129                 String[] split = string.split("\r\n");
130                 StringBuilder result = new StringBuilder();
131                 for(int i=0;i<split.length-7;i++) {
132                     String s = split[i].substring(9);
133                     if(s.equals(" ")) {
134                         continue;
135                     }else {
136                         result = result.append(s).append("\r\n");
137                     }
138                 }
139                 styledText.setText(result.toString());
140                 btnParse.setEnabled(false);
141                 /**
142                  * 另一种实现方式,用正则表达式匹配所有的注释,并删除
143                  * 但是此方法还没删除空白行
144                  */
145 //                Pattern pattern = Pattern.compile("/\\*{1,2}[\\S\\s]*?\\*/");
146 //                Matcher matcher = pattern.matcher(string);
147 //                styledText.setText(matcher.replaceAll(""));
148                 parseKey();
149             }
150             /**
151              * 此方法主要利用正则表达式为整个文本框中的代码中的关键字和引号中的字符串着色
152              */
153             private void parseKey() {
154                 String text = sourceViewer.getTextWidget().getText();
155                 Pattern pattern = Pattern.compile(REG);
156                 Matcher matcher = pattern.matcher(text);
157                 while(matcher.find()) {
158                     int start = matcher.start();
159                     int end = matcher.end();
160                     styledText.setStyleRange(new StyleRange(start, end-start, SWTResourceManager.getColor(SWT.COLOR_DARK_MAGENTA), null));
161                 }
162                 Pattern pattern2 = Pattern.compile("\".*\"");
163                 Matcher matcher2 = pattern2.matcher(text);
164                 while(matcher2.find()) {
165                     int start = matcher2.start();
166                     int end = matcher2.end();
167                     styledText.setStyleRange(new StyleRange(start, end-start, SWTResourceManager.getColor(SWT.COLOR_BLUE), null));
168                 }
169             }
170         });
171         
172         Button btnExport = new Button(composite, SWT.NONE);
173         btnExport.setText("导出");
174         btnExport.addSelectionListener(new SelectionAdapter() {
175             @Override
176             public void widgetSelected(SelectionEvent e) {
177                 FileDialog dialog = new FileDialog(shell,SWT.SAVE);
178                 dialog.setFilterExtensions(new String[] {"*.java"});
179                 dialog.setText("保存为...");
180                 String path = dialog.open();
181                 if(null==path) return;
182                 File file = new File(path);
183                 try {
184                     if(!file.exists()) {
185                         file.createNewFile();
186                     }
187                     FileWriter writer = new FileWriter(file);
188                     writer.write(styledText.getText());
189                     writer.close();
190                     MessageDialog.openInformation(shell, "导出成功!", "文本已成功导出至"+path+"目录下!");
191                 } catch (IOException e1) {
192                     e1.printStackTrace();
193                 }
194             }
195         });
196         
197         Button btnExit = new Button(composite, SWT.NONE);
198         btnExit.setText("关闭");
199         composite.setTabList(new Control[]{btnParse, btnExport, btnExit});
200         btnExit.addSelectionListener(new SelectionAdapter() {
201             @Override
202             public void widgetSelected(SelectionEvent e) {
203                 shell.dispose();
204             }
205         });
206         initData();
207     }
208     
209     private void initData() {
210         keywords.add("private");
211         keywords.add("void");
212         keywords.add("new");
213         keywords.add("this");
214         keywords.add("return");
215         keywords.add("class");
216         keywords.add("if");
217         
218         /**
219          * 配置内容助理
220          */
221         sourceViewer.configure(new SourceViewerConfiguration() {
222             @Override
223                public IContentAssistant getContentAssistant(ISourceViewer sourceViewer) {
224                 ContentAssistant contentAssistant = new ContentAssistant();
225                 contentAssistant.setInformationControlCreator(getInformationControlCreator(sourceViewer));
226                 contentAssistant.enableAutoActivation(true);
227                 contentAssistant.setContentAssistProcessor(new MyContentAssist(), IDocument.DEFAULT_CONTENT_TYPE);
228                 return contentAssistant;
229             }
230         });
231         /**
232          * 添加键盘监听,输入“Alt+/”时,打开内容助理
233          */
234         styledText.addVerifyKeyListener(new VerifyKeyListener() {
235             public void verifyKey(VerifyEvent event) {
236                 if(event.stateMask == SWT.ALT && event.character =='/') {
237                     if(sourceViewer.canDoOperation(SourceViewer.CONTENTASSIST_PROPOSALS)) {
238                         sourceViewer.doOperation(SourceViewer.CONTENTASSIST_PROPOSALS);
239                         event.doit = false;
240                     }
241                 }
242             }
243         });
244     }
245 }

 

内容助理类:

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;


public class MyContentAssist implements IContentAssistProcessor {

    @Override
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
        String[] keywords = new String[] {"continue","public","return","reta"};
        ITextSelection selection= (ITextSelection) viewer.getSelectionProvider().getSelection();
        if (selection.getOffset() == offset) {
            offset= selection.getOffset() + selection.getLength();
        }
        
        String text= extractPrefix(viewer, offset);
        List<String> list = new ArrayList<String>();
        for(String s:keywords) {
            if(text.length()<s.length()&&s.substring(0, text.length()).equalsIgnoreCase(text)) {
                list.add(s);
            }
        }
        if(list.size()>0) {
            CompletionProposal[] resu = new CompletionProposal[list.size()];
            for(int i=0;i<list.size();i++) {
                resu[i] = new CompletionProposal(list.get(i), offset-text.length(), text.length(), list.get(i).length());
            }
            return resu;
        }else {
            return new CompletionProposal[0];
        }
    }
    
    /**
     * 此方法是从源码中提取出来的,主要是提取当前光标所处位置到前面非Java字符的字符串
     * @param viewer
     * @param offset
     * @return
     */
    public static String extractPrefix(ITextViewer viewer, int offset) {
        int i= offset;
        IDocument document= viewer.getDocument();
        if (i > document.getLength())
            return ""; 

        try {
            while (i > 0) {
                char ch= document.getChar(i - 1);
                if (!Character.isJavaIdentifierPart(ch))
                    break;
                i--;
            }

            return document.get(i, offset - i);
        } catch (BadLocationException e) {
            return ""; 
        }
    }
    @Override
    public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
        return null;
    }

    @Override
    public char[] getCompletionProposalAutoActivationCharacters() {
        return null;
    }

    @Override
    public char[] getContextInformationAutoActivationCharacters() {
        return null;
    }

    @Override
    public String getErrorMessage() {
        return null;
    }

    @Override
    public IContextInformationValidator getContextInformationValidator() {
        return null;
    }

}

          要运行的话直接把这两个类放到一个包里运行主类就可以了。

 

posted @ 2018-01-31 10:43  墨梅一点清  阅读(1711)  评论(0编辑  收藏  举报