import java.awt.Color;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import com.google.common.collect.ImmutableMap;
import psl.dg.com.StringUtils;
/**
* 自定義的JTextPane,擴展了JTextPane沒有的一些功能(java關鍵字、單行註釋、多行註釋加亮)
*
* @author yangze
* @since 2019-09-15
*
*/
public class MyJTextPane extends JTextPane implements DocumentListener {
private Set<String> keywords;
private Map<String, Color> key2Color = new HashMap<String, Color>();
private Style keywordStyle;
private Style normalStyle;
private Style classNameStyle;
public MyJTextPane() {
super();
this.getDocument().addDocumentListener(this);
// 準備着色使用的樣式
/*keywordStyle = ((StyledDocument) getDocument()).addStyle(
"Keyword_Style", null);
StyleConstants.setForeground(keywordStyle, Color.BLUE);*/
normalStyle = ((StyledDocument) getDocument()).addStyle(
"Keyword_Style", null);
StyleConstants.setForeground(normalStyle, Color.BLACK);
key2Color = ImmutableMap.of("INFO", Color.GREEN, "ERROR", Color.RED,"WARNING",Color.YELLOW);
//獲取關鍵字
keywords = key2Color.keySet();
}
/**
* 設置全文本屬性
*
* @param attr
* @param replace
*/
public void setTextAttributes(AttributeSet attr, boolean replace) {
int p0 = 0;
int p1 = this.getText().length();
if (p0 != p1) {
StyledDocument doc = getStyledDocument();
doc.setCharacterAttributes(p0, p1 - p0, attr, replace);
} else {
MutableAttributeSet inputAttributes = getInputAttributes();
if (replace) {
inputAttributes.removeAttributes(inputAttributes);
}
inputAttributes.addAttributes(attr);
}
}
/**
* 單行註釋
*/
public void setSingleLineNoteCharacterAttributes() {
String text = this.getText();
int startPointer = 0;
int endPointer = 0;
if ((startPointer = text.indexOf("//")) == -1) {
return;
}
while ((endPointer = text.substring(startPointer).indexOf("\n")) != -1) {
endPointer = startPointer + endPointer;
if (startPointer >= endPointer) {
break;
}
int hangshu = text.substring(0, endPointer).split("\\n").length;
System.out.println("hangshu:" + hangshu);
SwingUtilities
.invokeLater(new ColouringWord(this, startPointer - hangshu
+ 1, endPointer - hangshu, new Color(63, 217, 95)));
startPointer = text.substring(endPointer + 1).indexOf("//");
startPointer = startPointer + endPointer + 1;
}
}
/**
* 多行註釋
*/
public void setMultiLineNoteCharacterAttributes() {
String text = this.getText();
int startPointer = 0;
int endPointer = 0;
if ((startPointer = text.indexOf("/*")) == -1) {
return;
}
while ((endPointer = text.substring(startPointer).indexOf("*/")) != -1) {
endPointer = startPointer + endPointer;
if (startPointer >= endPointer) {
break;
}
int hangshu = text.substring(0, endPointer).split("\\n").length;
int kuaju = text.substring(startPointer, endPointer).split("\\n").length;
SwingUtilities.invokeLater(new ColouringWord(this, startPointer
- hangshu + kuaju, endPointer + 3 - hangshu, new Color(63,
217, 95)));
startPointer = text.substring(endPointer + 1).indexOf("/*");
startPointer = startPointer + endPointer + 1;
}
}
/**
* 實時加亮關鍵字
* @param styledDocument
* @param pos
* @param len
* @throws BadLocationException
*/
public void myColouring(StyledDocument styledDocument, int pos, int len)
throws BadLocationException {
int start = indexOfWordStart(styledDocument, pos);
int end = indexOfWordEnd(styledDocument, pos + len);
char ch;
while (start < end) {
ch = getCharAt(styledDocument, start);
if (Character.isLetter(ch) || ch == '_') {//判斷是否爲字母
start = myColouringWord(styledDocument, start);
} else {
SwingUtilities.invokeLater(new ColouringTask(styledDocument,
start, 1, normalStyle));
++start;
}
}
}
/**
* 實時着色
*
* @param doc
* @param pos
* @return
* @throws BadLocationException
*/
public int myColouringWord(StyledDocument doc, int pos)
throws BadLocationException {
int wordEnd = indexOfWordEnd(doc, pos);
String word = doc.getText(pos, wordEnd - pos);
if (keywords.contains(word)) {
keywordStyle = ((StyledDocument) getDocument()).addStyle(
"Keyword_Style", null);
StyleConstants.setForeground(keywordStyle, key2Color.get(word));
SwingUtilities.invokeLater(new ColouringTask(doc, pos, wordEnd
- pos, keywordStyle));
} else {
SwingUtilities.invokeLater(new ColouringTask(doc, pos, wordEnd
- pos, normalStyle));
}
return wordEnd;
}
/**
* 取得在文檔中下標在pos處的字符.
*
* @param doc
* @param pos
* @return
* @throws BadLocationException
*/
public char getCharAt(Document doc, int pos) throws BadLocationException {
return doc.getText(pos, 1).charAt(0);
}
/**
* 取得下標爲pos時, 它所在的單詞開始的下標.
*
* @param doc
* @param pos
* @return
* @throws BadLocationException
*/
public int indexOfWordStart(Document doc, int pos)
throws BadLocationException {
// 從pos開始向前找到第一個非單詞字符.
for (; pos > 0 && isWordCharacter(doc, pos - 1); --pos)
;
return pos;
}
public void appendLine(String text) {
String str = this.getText();
if(StringUtils.isBlank(str)) {
this.setText(text);
}
else{
this.setText(str + "\n" + text);
}
}
/**
* 取得下標爲pos時, 它所在的單詞結束的下標.
*
* @param doc
* @param pos
* @return
* @throws BadLocationException
*/
public int indexOfWordEnd(Document doc, int pos)
throws BadLocationException {
// 從pos開始向前找到第一個非單詞字符.
for (; isWordCharacter(doc, pos); ++pos)
;
return pos;
}
/**
* 如果一個字符是字母, 數字, 下劃線, 則返回true.
*
* @param doc
* @param pos
* @return
* @throws BadLocationException
*/
public boolean isWordCharacter(Document doc, int pos)
throws BadLocationException {
char ch = getCharAt(doc, pos);
if (Character.isLetter(ch) || Character.isDigit(ch) || ch == '_') {
return true;
}
return false;
}
@Override
// 給出屬性或屬性集發生了更改的通知
public void changedUpdate(DocumentEvent e) {
}
@Override
// 給出對文檔執行了插入操作的通知
public void insertUpdate(DocumentEvent e) {
try {
myColouring((StyledDocument) e.getDocument(), e.getOffset(),
e.getLength());
// noteFinder.ColorNote(this.getText());// 給註釋上色
setSingleLineNoteCharacterAttributes();
setMultiLineNoteCharacterAttributes();
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
@Override
// 給出移除了一部分文檔的通知
public void removeUpdate(DocumentEvent e) {
try {
// 因爲刪除後光標緊接着影響的單詞兩邊, 所以長度就不需要了
myColouring((StyledDocument) e.getDocument(), e.getOffset(), 0);
// noteFinder.ColorNote(this.getText());// 給註釋上色
setSingleLineNoteCharacterAttributes();
setMultiLineNoteCharacterAttributes();
} catch (BadLocationException e1) {
e1.printStackTrace();
}
}
/**
* 多線程繪製顏色
*
*
*
*/
private class ColouringTask implements Runnable {
private StyledDocument doc;
private Style style;
private int pos;
private int len;
public ColouringTask(StyledDocument doc, int pos, int len, Style style) {
this.doc = doc;
this.pos = pos;
this.len = len;
this.style = style;
}
public void run() {
try {
// 這裏就是對字符進行着色
doc.setCharacterAttributes(pos, len, style, false);
} catch (Exception e) {
}
}
}
}
/**
* 多線程繪製顏色
*/
class ColouringWord implements Runnable {
private int startPointer;
private int endPointer;
private Color color;
private JTextPane jTextPane;
public ColouringWord(JTextPane jTextPane, int pos, int len, Color color) {
this.jTextPane = jTextPane;
this.startPointer = pos;
this.endPointer = len;
this.color = color;
}
@Override
public void run() {
SimpleAttributeSet attributeSet = new SimpleAttributeSet();
StyleConstants.setForeground(attributeSet, color);
boolean replace = false;
int p0 = startPointer;
int p1 = endPointer;
if (p0 != p1) {
StyledDocument doc = jTextPane.getStyledDocument();
doc.setCharacterAttributes(p0, p1 - p0, attributeSet, replace);
} else {
MutableAttributeSet inputAttributes = jTextPane
.getInputAttributes();
if (replace) {
inputAttributes.removeAttributes(inputAttributes);
}
inputAttributes.addAttributes(attributeSet);
}
}
}