org.fife.ui.rsyntaxtextarea.SyntaxScheme Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rsyntaxtextarea Show documentation
Show all versions of rsyntaxtextarea Show documentation
RSyntaxTextArea is the syntax highlighting text editor for Swing applications. Features include syntax highlighting for 40+ languages, code folding, code completion, regex find and replace, macros, code templates, undo/redo, line numbering and bracket matching.
/* * 02/26/2004 * * SyntaxScheme.java - The set of colors and tokens used by an RSyntaxTextArea * to color tokens. * Copyright (C) 2004 Robert Futrell * robert_futrell at users.sourceforge.net * http://fifesoft.com/rsyntaxtextarea * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ package org.fife.ui.rsyntaxtextarea; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import javax.swing.text.StyleContext; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; /** * The set of colors and styles used by an
if this color scheme and *RSyntaxTextArea
to * color tokens. * * @author Robert Futrell * @version 1.0 */ public class SyntaxScheme implements Cloneable { public Style[] styles; private static final String VERSION = "*ver1"; /** * Creates a color scheme that either has all color values set to * a default value or set tonull
. * * @param useDefaults Iftrue
, all color values will * be set to default colors; iffalse
, all colors * will be initiallynull
. */ public SyntaxScheme(boolean useDefaults) { styles = new Style[Token.NUM_TOKEN_TYPES]; if (useDefaults) { restoreDefaults(null); } } /** * Creates a default color scheme. * * @param baseFont The base font to use. Keywords will be a bold version * of this font, and comments will be an italicized version of this * font. */ public SyntaxScheme(Font baseFont) { styles = new Style[Token.NUM_TOKEN_TYPES]; restoreDefaults(baseFont); } /** * Changes the "base font" for this syntax scheme. This is called by *RSyntaxTextArea
when its font changes via *setFont()
. This looks for tokens that use a derivative of * the text area's old font (but bolded and/or italicized) and make them * use the new font with those stylings instead. This is desirable because * most programmers prefer a single font to be used in their text editor, * but might want bold (say for keywords) or italics. * * @param old The old font of the text area. * @param font The new font of the text area. */ void changeBaseFont(Font old, Font font) { for (int i=0; itrue otherScheme
are the same scheme; *false
otherwise. */ public boolean equals(Object otherScheme) { // No need for null check; instanceof takes care of this for us, // i.e. "if (!(null instanceof Foo))" evaluates to "true". if (!(otherScheme instanceof SyntaxScheme)) { return false; } Style[] otherSchemes = ((SyntaxScheme)otherScheme).styles; int length = styles.length; for (int i=0; i"$rrggbb". * * @param c The color. * @return The string representation of the color. */ private static final String getHexString(Color c) { return "$" + Integer.toHexString((c.getRGB() & 0xffffff)+0x1000000). substring(1); } /** * This is implemented to be consistent with {@link #equals(Object)}. * This is a requirement to keep FindBugs happy. * * @return The hash code for this object. */ public int hashCode() { // Keep me fast. Iterating over *all* syntax schemes contained is // probably much slower than a "bad" hash code here. int hashCode = 0; int count = styles.length; for (int i=0; i null, a default monospaced font is used. * @param in The stream to load from. It is up to the caller to close this * stream when they are done. * @return The syntax scheme. * @throws IOException If an IO error occurs. */ public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException { if (baseFont==null) { baseFont = RSyntaxTextArea.getDefaultFont(); } return XmlParser.load(baseFont, in); } /** * Loads a syntax highlighting color scheme from a string created from * toCommaSeparatedString
. This method is useful for saving * and restoring color schemes. * * @param string A string generated from {@link #toCommaSeparatedString()}. * @return A color scheme. */ public static SyntaxScheme loadFromString(String string) { SyntaxScheme scheme = new SyntaxScheme(true); try { if (string!=null) { String[] tokens = string.split(",", -1); // Check the version string, use defaults if incompatible if (tokens.length==0 || !VERSION.equals(tokens[0])) { return scheme; // Still set to defaults } int tokenTypeCount = Token.NUM_TOKEN_TYPES; int tokenCount = tokenTypeCount*7 + 1; // Version string if (tokens.length!=tokenCount) { throw new Exception( "Not enough tokens in packed color scheme: expected " + tokenCount + ", found " + tokens.length); } // Loop through each token style. Format: // "index,(fg|-),(bg|-),(t|f),((font,style,size)|(-,,))" for (int i=0; ikeep fg as null fg = stringToColor(temp); } Color bg = null; temp = tokens[pos+2]; if (!"-".equals(temp)) { // "-" => keep bg as null bg = stringToColor(temp); } // Check for "true" or "false" since we don't want to // accidentally suck in an int representing the next // packed color, and any string != "true" means false. temp = tokens[pos+3]; if (!"t".equals(temp) && !"f".equals(temp)) throw new Exception("Expected 't' or 'f', found " + temp); boolean underline = "t".equals(temp); Font font = null; String family = tokens[pos+4]; if (!"-".equals(family)) { font = new Font(family, Integer.parseInt(tokens[pos+5]), // style Integer.parseInt(tokens[pos+6])); // size } scheme.styles[i] = new Style(fg, bg, font, underline); } } } catch (Exception e) { e.printStackTrace(); } return scheme; } void refreshFontMetrics(Graphics2D g2d) { // It is assumed that any rendering hints are already applied to g2d. for (int i=0; i null, then a default monospaced font is * used. */ public void restoreDefaults(Font baseFont) { // Colors used by tokens. Color comment = new Color(0,128,0); Color docComment = new Color(164,0,0); Color keyword = Color.BLUE; Color function = new Color(173,128,0); Color literalNumber = new Color(100,0,200); Color literalString = new Color(220,0,156); Color error = new Color(148,148,0); // Special fonts. if (baseFont==null) { baseFont = RSyntaxTextArea.getDefaultFont(); } // WORKAROUND for Sun JRE bug 6282887 (Asian font bug in 1.4/1.5) StyleContext sc = StyleContext.getDefaultStyleContext(); Font boldFont = sc.getFont(baseFont.getFamily(), Font.BOLD, baseFont.getSize()); Font italicFont = sc.getFont(baseFont.getFamily(), Font.ITALIC, baseFont.getSize()); Font commentFont = italicFont;//baseFont.deriveFont(Font.ITALIC); Font keywordFont = boldFont;//baseFont.deriveFont(Font.BOLD); styles[Token.COMMENT_EOL] = new Style(comment, null, commentFont); styles[Token.COMMENT_MULTILINE] = new Style(comment, null, commentFont); styles[Token.COMMENT_DOCUMENTATION] = new Style(docComment, null, commentFont); styles[Token.RESERVED_WORD] = new Style(keyword, null, keywordFont); styles[Token.FUNCTION] = new Style(function, null); styles[Token.LITERAL_BOOLEAN] = new Style(literalNumber, null); styles[Token.LITERAL_NUMBER_DECIMAL_INT] = new Style(literalNumber, null); styles[Token.LITERAL_NUMBER_FLOAT] = new Style(literalNumber, null); styles[Token.LITERAL_NUMBER_HEXADECIMAL] = new Style(literalNumber, null); styles[Token.LITERAL_STRING_DOUBLE_QUOTE] = new Style(literalString, null); styles[Token.LITERAL_CHAR] = new Style(literalString, null); styles[Token.LITERAL_BACKQUOTE] = new Style(literalString, null); styles[Token.DATA_TYPE] = new Style(new Color(0,128,128), null); styles[Token.VARIABLE] = new Style(new Color(255,153,0), null); styles[Token.IDENTIFIER] = new Style(null, null); styles[Token.WHITESPACE] = new Style(Color.gray, null); styles[Token.SEPARATOR] = new Style(Color.RED, null); styles[Token.OPERATOR] = new Style(new Color(128,64,64), null); styles[Token.PREPROCESSOR] = new Style(new Color(128,128,128), null); styles[Token.MARKUP_TAG_DELIMITER] = new Style(Color.RED, null); styles[Token.MARKUP_TAG_NAME] = new Style(Color.BLUE, null); styles[Token.MARKUP_TAG_ATTRIBUTE] = new Style(new Color(63,127,127), null); // styles[Token.ERROR] = null; styles[Token.ERROR_IDENTIFIER] = new Style(error, null); styles[Token.ERROR_NUMBER_FORMAT] = new Style(error, null); styles[Token.ERROR_STRING_DOUBLE] = new Style(error, null); styles[Token.ERROR_CHAR] = new Style(error, null); } /** * Sets a style to use when rendering a token type. * * @param type The token type. * @param style The style for the token type. */ public void setStyle(int type, Style style) { styles[type] = style; } /** * Returns the color represented by a string. If the first char in the * string is ' $
', it is assumed to be in hex, otherwise it is * assumed to be decimal. So, for example, both of these: ** "$00ff00" * "65280" ** will returnnew Color(0, 255, 0)
. * * @param s The string to evaluate. * @return The color. */ private static final Color stringToColor(String s) { // Check for decimal as well as hex, for backward // compatibility (fix from GwynEvans on forums) char ch = s.charAt(0); return new Color((ch=='$' || ch=='#') ? Integer.parseInt(s.substring(1),16) : Integer.parseInt(s)); } /** * Returns this syntax highlighting scheme as a comma-separated list of * values as follows: **
* * @return A string representing the rgb values of the colors. */ public String toCommaSeparatedString() { StringBuffer sb = new StringBuffer(VERSION); sb.append(','); for (int i=0; i- If a color is non-null, it is added as a 24-bit integer * of the form
((r<<16) | (g<<8) | (b))
; if it is *null
, it is added as "-,". *- The font and style (bold/italic) is added as an integer like so: * "family, style, size". *
- The entire syntax highlighting scheme is thus one long string of * color schemes of the format "i,[fg],[bg],uline,[style], * where: *
*
*i
is the index of the syntax scheme. *- fg and bg are the foreground and background * colors for the scheme, and may be null (represented by *
-
). *uline
is whether or not the font should be * underlined, and is eithert
orf
. *style
is thefamily,style,size
* triplet described above. *SyntaxScheme from an XML file. */ private static class XmlParser extends DefaultHandler { private Font baseFont; private SyntaxScheme scheme; public XmlParser(Font baseFont) { scheme = new SyntaxScheme(baseFont); } /** * Creates the XML reader to use. Note that in 1.4 JRE's, the reader * class wasn't defined by default, but in 1.5+ it is. * * @return The XML reader to use. */ private static XMLReader createReader() throws IOException { XMLReader reader = null; try { reader = XMLReaderFactory.createXMLReader(); } catch (SAXException e) { // Happens in JRE 1.4.x; 1.5+ define the reader class properly try { reader = XMLReaderFactory.createXMLReader( "org.apache.crimson.parser.XMLReaderImpl"); } catch (SAXException se) { throw new IOException(se.toString()); } } return reader; } public static SyntaxScheme load(Font baseFont, InputStream in) throws IOException { XMLReader reader = createReader(); XmlParser parser = new XmlParser(baseFont); parser.baseFont = baseFont; reader.setContentHandler(parser); InputSource is = new InputSource(in); is.setEncoding("UTF-8"); try { reader.parse(is); } catch (SAXException se) { throw new IOException(se.toString()); } return parser.scheme; } public void startElement(String uri, String localName, String qName, Attributes attrs) { if ("style".equals(qName)) { String type = attrs.getValue("token"); Field field = null; try { field = Token.class.getField(type); } catch (RuntimeException re) { throw re; // FindBugs } catch (Exception e) { System.err.println("Invalid token type: " + type); return; } if (field.getType()==int.class) { int index = 0; try { index = field.getInt(scheme); } catch (IllegalArgumentException e) { e.printStackTrace(); return; } catch (IllegalAccessException e) { e.printStackTrace(); return; } String fgStr = attrs.getValue("fg"); if (fgStr!=null) { Color fg = stringToColor(fgStr); scheme.styles[index].foreground = fg; } String bgStr = attrs.getValue("bg"); if (bgStr!=null) { Color bg = stringToColor(bgStr); scheme.styles[index].background = bg; } boolean styleSpecified = false; boolean bold = false; boolean italic = false; String boldStr = attrs.getValue("bold"); if (boldStr!=null) { bold = Boolean.valueOf(boldStr).booleanValue(); styleSpecified = true; } String italicStr = attrs.getValue("italic"); if (italicStr!=null) { italic = Boolean.valueOf(italicStr).booleanValue(); styleSpecified = true; } if (styleSpecified) { int style = 0; if (bold) { style |= Font.BOLD; } if (italic) { style |= Font.ITALIC; } scheme.styles[index].font = baseFont.deriveFont(style); } String ulineStr = attrs.getValue("underline"); if (ulineStr!=null) { boolean uline= Boolean.valueOf(ulineStr).booleanValue(); scheme.styles[index].underline = uline; } } } } } }