package at.atc.ows.client.application.wiki.reader;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.Position;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyledEditorKit;
public class WikiTextPane extends JEditorPane
{
public static final Object LINK = new StringBuffer("link");
private static final WikiTextParsers[] PARSERS = {
new BoldParser(),
new LinkParser(),
new ItalicParser(),
new UnderlineParser(),
new NamedLinkParser()
};
private StyledDocument document;
private WikiTextPaneModel model;
public WikiTextPane(WikiTextPaneModel model)
{
setEditorKit(new StyledEditorKit());
MouseAdapter mouseController = new MouseController();
addMouseListener(mouseController);
addMouseMotionListener(mouseController);
document = (StyledDocument) getDocument();
this.model = model;
}
public void parseString(String text)
{
int offset = 0;
try {
document.remove(0, document.getLength());
while(text != null && text.length() != 0) {
int firstCodeParserLocation = Integer.MAX_VALUE;
WikiTextParsers firstCodeParser = null;
for(WikiTextParsers parsers:PARSERS) {
int parserLocation = parsers.getFirstMatchLocation(text);
if(parserLocation != -1 && parserLocation < firstCodeParserLocation) {
firstCodeParser = parsers;
firstCodeParserLocation = parserLocation;
}
}
if(firstCodeParser == null) {
document.insertString(offset, text, null);
text = null;
continue;
}
String beforTextPiece = text.substring(0, firstCodeParserLocation);
document.insertString(offset, beforTextPiece, null);
offset+= beforTextPiece.length();
text = text.substring(firstCodeParserLocation);
String[] result = firstCodeParser.addTextToDocument(offset, text, document, model);
text = result[0];
offset = Integer.parseInt(result[1]);
}
}
catch (BadLocationException e)
{
e.printStackTrace();
}
}
private class MouseController extends MouseAdapter
{
private boolean handCursor;
public void mouseMoved(MouseEvent e)
{
Element c = characterElementAt(e);
if (c != null) {
if(c.getAttributes().getAttribute(LINK) != null) {
if(!handCursor) {
setCursor(new Cursor(Cursor.HAND_CURSOR));
handCursor = true;
}
}
else if(handCursor) {
setCursor(new Cursor(Cursor.TEXT_CURSOR));
handCursor = false;
}
}
}
public void mousePressed(MouseEvent e)
{
if (!SwingUtilities.isLeftMouseButton(e))
return;
Element c = characterElementAt(e);
if(c == null) {
return;
}
Object linkAttribute = c.getAttributes().getAttribute(LINK);
if(linkAttribute != null) {
model.openArticle(linkAttribute.toString());
}
}
private Element characterElementAt(MouseEvent e)
{
JEditorPane p = (JEditorPane) e.getComponent();
Position.Bias[] bias = new Position.Bias[1];
int position = p.getUI().viewToModel(p, e.getPoint(), bias);
if (bias[0] == Position.Bias.Backward && position != 0) {
position--;
}
Element c = ((StyledDocument) p.getDocument()).getCharacterElement(position);
return c;
}
}
private static class LinkParser extends WikiTextParsers
{
public String getRegEx()
{
return("(.*?)\\[article\\](.+?)\\[/article\\](.*?)");
}
public int getFirstMatchLocation(String text)
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() == 0) {
return(-1);
}
return(matcher.group(1).length());
}
public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() < 2) {
return(new String[] {
text, String.valueOf(offset)
});
}
String matchText = matcher.group(2);
String postText = matcher.group(3);
MutableAttributeSet attributeSet = new SimpleAttributeSet();
attributeSet.addAttribute(LINK, matchText);
attributeSet.addAttribute(StyleConstants.Foreground, model.existArticle(matchText) ? Color.BLUE : Color.RED);
document.insertString(offset, matchText, attributeSet);
offset+= matchText.length();
return(new String[] {
postText, String.valueOf(offset)
});
}
}
private static class NamedLinkParser extends WikiTextParsers
{
public String getRegEx()
{
return("(.*?)\\[article\\=(.+?)\\](.+?)\\[/article\\](.*?)");
}
public int getFirstMatchLocation(String text)
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() == 0) {
return(-1);
}
return(matcher.group(1).length());
}
public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches()) {
return(new String[] {
text, String.valueOf(offset)
});
}
String articleName = matcher.group(2);
String linkText = matcher.group(3);
String postText = matcher.group(4);
MutableAttributeSet attributeSet = new SimpleAttributeSet();
attributeSet.addAttribute(LINK, articleName);
attributeSet.addAttribute(StyleConstants.Foreground, model.existArticle(articleName) ? Color.BLUE : Color.RED);
document.insertString(offset, linkText, attributeSet);
offset+= linkText.length();
return(new String[] {
postText, String.valueOf(offset)
});
}
}
private static class ItalicParser extends WikiTextParsers
{
public String getRegEx()
{
return("(.*?)\\[i\\](.+?)\\[/i\\](.*?)");
}
public int getFirstMatchLocation(String text)
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() == 0) {
return(-1);
}
return(matcher.group(1).length());
}
public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() < 2) {
return(new String[] {
text, String.valueOf(offset)
});
}
String matchText = matcher.group(2);
String postText = matcher.group(3);
MutableAttributeSet attributeSet = new SimpleAttributeSet();
attributeSet.addAttribute(StyleConstants.Italic, true);
document.insertString(offset, matchText, attributeSet);
offset+= matchText.length();
return(new String[] {
postText, String.valueOf(offset)
});
}
}
private static class UnderlineParser extends WikiTextParsers
{
public String getRegEx()
{
return("(.*?)\\[u\\](.+?)\\[/u\\](.*?)");
}
public int getFirstMatchLocation(String text)
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() == 0) {
return(-1);
}
return(matcher.group(1).length());
}
public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() < 2) {
return(new String[] {
text, String.valueOf(offset)
});
}
String matchText = matcher.group(2);
String postText = matcher.group(3);
MutableAttributeSet attributeSet = new SimpleAttributeSet();
attributeSet.addAttribute(StyleConstants.Underline, true);
document.insertString(offset, matchText, attributeSet);
offset+= matchText.length();
return(new String[] {
postText, String.valueOf(offset)
});
}
}
private static class BoldParser extends WikiTextParsers
{
public String getRegEx()
{
return("(.*?)\\[b\\](.+?)\\[/b\\](.*?)");
}
public int getFirstMatchLocation(String text)
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() == 0) {
return(-1);
}
return(matcher.group(1).length());
}
public String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException
{
Matcher matcher = pattern.matcher(text);
if(!matcher.matches() || matcher.groupCount() < 2) {
return(new String[] {
text, String.valueOf(offset)
});
}
String matchText = matcher.group(2);
String postText = matcher.group(3);
MutableAttributeSet attributeSet = new SimpleAttributeSet();
attributeSet.addAttribute(StyleConstants.Bold, true);
document.insertString(offset, matchText, attributeSet);
offset+= matchText.length();
return(new String[] {
postText, String.valueOf(offset)
});
}
}
private static abstract class WikiTextParsers
{
protected Pattern pattern;
public WikiTextParsers()
{
super();
pattern = Pattern.compile(getRegEx(), Pattern.DOTALL);
}
public abstract String[] addTextToDocument(int offset, String text, Document document, WikiTextPaneModel model) throws BadLocationException;
public abstract int getFirstMatchLocation(String text);
public abstract String getRegEx();
}
public static abstract class WikiTextPaneModel
{
public abstract void openArticle(String articleTitle);
public abstract boolean existArticle(String articleTitle);
}
}