package org.xmind.ui.richtext; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.rules.IToken; import org.eclipse.jface.text.rules.ITokenScanner; import org.eclipse.jface.text.rules.Token; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.widgets.Display; public class RichTextScanner implements ITokenScanner { private static final String regex = "\\b(https?|ftp|mailto|svn|irc|gopher|telnet|nntp|worldwind|news):(?:/{1,3})([\\w-]+\\.)+[\\w-]+[\\w-._/\\~%-+&#?!=()@:]*";//$NON-NLS-1$ private IRichDocument document; private int offset; private int length; private StyleRange[] textStyles; private int styleIndex; private Hyperlink[] hyperlinks; private int hyperlinkIndex; private int lastOffset; private int lastLength; private List<AutoHyperlink> autoHyperRanges; private int autoIndex; public void setRange(IDocument document, int offset, int length) { if (!(document instanceof IRichDocument)) return; this.document = (IRichDocument) document; this.offset = offset; this.length = length; this.textStyles = this.document.getTextStyles(); this.styleIndex = 0; this.hyperlinks = this.document.getHyperlinks(); this.hyperlinkIndex = 0; this.lastOffset = offset; this.lastLength = 0; getHyperlinkAt(offset); refreshAutoHyperlinks(); this.autoIndex = 0; } public int getTokenLength() { return lastLength; } public int getTokenOffset() { return lastOffset; } public IToken nextToken() { int current = lastOffset + lastLength; if (current >= offset + length) { return Token.EOF; } int next = offset + length; Hyperlink h = getHyperlinkAt(current); AutoHyperlink a = getAutoHyperlinkAt(current); int hyperNext = 0; if (h != null) { if (current >= h.start) { hyperNext = Math.min(next, h.start + h.length); } else { hyperNext = Math.min(next, h.start); } } int autoNext = 0; if (a != null) { if (current >= a.start) autoNext = Math.min(next, a.start + a.length); else autoNext = Math.min(next, a.start); } int tempStart = 0, tempLength = 0; if (h != null && a != null) { if (h.start < a.start) { next = hyperNext; tempStart = h.start; tempLength = h.length; } else { next = autoNext; tempStart = a.start; tempLength = a.length; } } else if (h != null && a == null) { next = hyperNext; tempStart = h.start; tempLength = h.length; } else if (a != null && h == null) { next = autoNext; tempStart = a.start; tempLength = a.length; } StyleRange style = getTextStyleAt(current); if (style != null) { if (current >= style.start) { next = Math.min(next, style.start + style.length); } else { next = Math.min(next, style.start); } } this.lastOffset = current; this.lastLength = next - current; if (lastLength <= 0) { return Token.EOF; } if (!(style != null && current >= style.start && next <= style.start + style.length)) { style = null; } // if (h != null && current >= h.start && next <= h.start + h.length) { // if (a != null && current >= a.start && next <= a.start + a.len) { if (h != null || a != null) { if (current >= tempStart && next <= tempStart + tempLength) { if (style == null) style = RichTextUtils.DEFAULT_STYLE; style = (StyleRange) style.clone(); if (style.foreground == null || style.foreground == RichTextUtils.DEFAULT_FOREGROUND || style.foreground .equals(RichTextUtils.DEFAULT_FOREGROUND)) { style.foreground = Display.getCurrent() .getSystemColor(SWT.COLOR_BLUE); } style.underline = true; } } return new Token(style); } public IToken nextToken1() { int current = lastOffset + lastLength; if (current >= offset + length) { return Token.EOF; } int next = offset + length; Hyperlink h = getHyperlinkAt(current); if (h != null) { if (current >= h.start) { next = Math.min(next, h.start + h.length); } else { next = Math.min(next, h.start); } } StyleRange style = getTextStyleAt(current); if (style != null) { if (current >= style.start) { next = Math.min(next, style.start + style.length); } else { next = Math.min(next, style.start); } } this.lastOffset = current; this.lastLength = next - current; if (lastLength <= 0) { return Token.EOF; } if (!(style != null && current >= style.start && next <= style.start + style.length)) { style = null; } if (h != null && current >= h.start && next <= h.start + h.length) { if (style == null) style = RichTextUtils.DEFAULT_STYLE; style = (StyleRange) style.clone(); if (style.foreground == null || style.foreground == RichTextUtils.DEFAULT_FOREGROUND || style.foreground .equals(RichTextUtils.DEFAULT_FOREGROUND)) { style.foreground = Display.getCurrent() .getSystemColor(SWT.COLOR_BLUE); } style.underline = true; } return new Token(style); } private Hyperlink getHyperlinkAt(int position) { while (hyperlinkIndex < hyperlinks.length) { Hyperlink h = hyperlinks[hyperlinkIndex]; if (position < h.start + h.length) return h; hyperlinkIndex++; } return null; } private AutoHyperlink getAutoHyperlinkAt(int position) { if (autoHyperRanges == null) return null; AutoHyperlink[] autoArray = autoHyperRanges .toArray(new AutoHyperlink[0]); while (autoIndex < autoArray.length) { AutoHyperlink autoHyperRange = autoArray[autoIndex]; if (position < autoHyperRange.start + autoHyperRange.length) return autoHyperRange; autoIndex++; } return null; } private StyleRange getTextStyleAt(int position) { while (styleIndex < textStyles.length) { StyleRange style = textStyles[styleIndex]; if (position < style.start + style.length) return style; styleIndex++; } return null; } private void refreshAutoHyperlinks() { if (autoHyperRanges != null) autoHyperRanges = null; String content = document.get(); Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); while (matcher.find()) { int start = matcher.start(); int length = matcher.end() - start; if (autoHyperRanges == null) autoHyperRanges = new ArrayList<AutoHyperlink>(); autoHyperRanges.add(new AutoHyperlink(start, length)); } } }