/*
* Copyright 2014, Synthuse.org
* Released under the Apache Version 2.0 License.
*
* last modified by ejakubowski
*/
package org.synthuse;
import java.awt.Color;
import java.awt.Graphics;
import java.util.*;
import java.util.regex.*;
import javax.swing.text.*;
/* Example:
// Set editor kit
jtextpane.setEditorKitForContentType("text/xml", new XmlEditorKit());
jtextpane.setContentType("text/xml");
*/
public class XmlEditorKit extends StyledEditorKit {
private static final long serialVersionUID = 2969169649596107757L;
private ViewFactory xmlViewFactory;
private static HashMap<Pattern, Color> patternColors;
private static String TAG_PATTERN = "(</?[a-z]*)\\s?>?";
private static String TAG_END_PATTERN = "(/>)";
private static String TAG_ATTRIBUTE_PATTERN = "\\s([a-zA-Z0-9]*)\\s*?\\=\\s*?\\\"";
private static String TAG_ATTRIBUTE_VALUE = "[a-zA-Z0-9]*\\=(\"[^\"]*\")";
private static String TAG_COMMENT = "(<!--.*-->)";
private static String TAG_CDATA_START = "(\\<!\\[CDATA\\[).*";
private static String TAG_CDATA_END = ".*(]]>)";
//public static String TAG_HIGHLIGHTED = ".*hwnd=\"([^\"]*)\".*";
public static String TAG_HIGHLIGHTED = "";
public static Color TAG_HIGHLIGHTED_COLOR = new Color(170, 255, 48);
public static int HIGHLIGHTED_START = 0;
public static int HIGHLIGHTED_END = 0;
public static Color HIGHLIGHTED_COLOR = new Color(240, 255, 112);
static {
// NOTE: the order is important!
patternColors = new HashMap<Pattern, Color>();
patternColors.put(Pattern.compile(TAG_CDATA_START), new Color(128, 128, 128));
patternColors.put(Pattern.compile(TAG_CDATA_END), new Color(128, 128, 128));
patternColors.put(Pattern.compile(TAG_PATTERN), new Color(63, 127, 127));
patternColors.put(Pattern.compile(TAG_ATTRIBUTE_PATTERN), new Color(127, 0, 127));
patternColors.put(Pattern.compile(TAG_END_PATTERN), new Color(63, 127, 127));
patternColors.put(Pattern.compile(TAG_ATTRIBUTE_VALUE), new Color(42, 0, 255));
patternColors.put(Pattern.compile(TAG_COMMENT), new Color(63, 95, 191));
}
class XmlView extends PlainView {
public XmlView(Element element) {
super(element);
// Set tabsize to 4 (instead of the default 8)
getDocument().putProperty(PlainDocument.tabSizeAttribute, 4);
}
@Override
protected int drawUnselectedText(Graphics graphics, int x, int y, int p0, int p1) throws BadLocationException {
try {
Document doc = getDocument();
String text = doc.getText(p0, p1 - p0);
Segment segment = getLineBuffer();
int initialXpos = x;
SortedMap<Integer, Integer> startMap = new TreeMap<Integer, Integer>();
SortedMap<Integer, Color> colorMap = new TreeMap<Integer, Color>();
// Match all regexes on this snippet, store positions
for (Map.Entry<Pattern, Color> entry : patternColors.entrySet()) {
Matcher matcher = entry.getKey().matcher(text);
while (matcher.find()) {
startMap.put(matcher.start(1), matcher.end(1));
colorMap.put(matcher.start(1), entry.getValue());
}
}
// TODO: check the map for overlapping parts
int i = 0;
//add tag highlighted background colors
if (!TAG_HIGHLIGHTED.isEmpty()) {
Matcher highlightMatcher = Pattern.compile(TAG_HIGHLIGHTED).matcher(text);
while(highlightMatcher.find()) {
int start = highlightMatcher.start(1);
int end = highlightMatcher.end(1);
if (i < start) {
graphics.setColor(Color.black);
doc.getText(p0 + i, start - i, segment);
x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
}
graphics.setColor(TAG_HIGHLIGHTED_COLOR);
i = end;
doc.getText(p0 + start, i - start, segment);
int width = Utilities.getTabbedTextWidth(segment, graphics.getFontMetrics(), x, this, p0 + start);
//graphics.drawLine(x, y, width, y);graphics.getFontMetrics()
graphics.fillRect(x, y - graphics.getFontMetrics().getHeight() + 2, width, graphics.getFontMetrics().getHeight());
graphics.setColor(Color.black);
doc.getText(p0 + start, i - start, segment);
x = Utilities.drawTabbedText(segment, x, y, graphics, this, start);
}
}
x = initialXpos;
i=0;
//add highlighted background colors based on position
//String textx = doc.getText(p0, p1 - p0);
if ((HIGHLIGHTED_START < p1 && HIGHLIGHTED_START >= p0) || (HIGHLIGHTED_END <= p1 && HIGHLIGHTED_END > p0) || ( HIGHLIGHTED_START < p1 && HIGHLIGHTED_END > p0)) {
//select whole line
int start = 0;
int end = text.length();
// test to see if only partial line is needed.
if (HIGHLIGHTED_START > p0)
start = HIGHLIGHTED_START - p0;
if (HIGHLIGHTED_END < p1)
end -= p1 - HIGHLIGHTED_END;
if (i < start) { // fill in normal color if start highlight isn't at the beginning
graphics.setColor(Color.black);
doc.getText(p0 + i, start - i, segment);
x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
}
graphics.setColor(HIGHLIGHTED_COLOR);// fill in the highlight color
i = end;
if (i - start > 0) {
doc.getText(p0 + start, i - start, segment);
int width = Utilities.getTabbedTextWidth(segment, graphics.getFontMetrics(), x, this, p0 + start);
//graphics.drawLine(x, y, width, y);graphics.getFontMetrics()
graphics.fillRect(x, y - graphics.getFontMetrics().getHeight() + 2, width, graphics.getFontMetrics().getHeight());
graphics.setColor(Color.black);
doc.getText(p0 + start, i - start, segment);
x = Utilities.drawTabbedText(segment, x, y, graphics, this, start);
}
//else
// System.out.println("invalid highlighting " + (i - start) + " is <= 0 (" + p0 + "-" + p1 + "=" + (p1 - p0) +") " + start + ", " + end + " len=" + text.length());
}
x = initialXpos;
i=0;
// Color the parts of xml foreground font
for (Map.Entry<Integer, Integer> entry : startMap.entrySet()) {
int start = entry.getKey();
int end = entry.getValue();
if (i < start) {
graphics.setColor(Color.black);
doc.getText(p0 + i, start - i, segment);
x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
}
graphics.setColor(colorMap.get(start));
i = end;
doc.getText(p0 + start, i - start, segment);
x = Utilities.drawTabbedText(segment, x, y, graphics, this, start);
}
// Paint possible remaining text black
if (i < text.length()) {
graphics.setColor(Color.black);
doc.getText(p0 + i, text.length() - i, segment);
x = Utilities.drawTabbedText(segment, x, y, graphics, this, i);
}
} catch (Exception e) {
e.printStackTrace();
}
return x;
}
}
class XmlViewFactory extends Object implements ViewFactory {
/**
* @see javax.swing.text.ViewFactory#create(javax.swing.text.Element)
*/
public View create(Element element) {
return new XmlView(element);
}
}
public XmlEditorKit() {
xmlViewFactory = new XmlViewFactory();
}
@Override
public ViewFactory getViewFactory() {
return xmlViewFactory;
}
@Override
public String getContentType() {
return "text/xml";
}
}