All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.ui.rsyntaxtextarea.SyntaxScheme Maven / Gradle / Ivy

Go to download

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.

There is a newer version: 3.5.1
Show newest version
/*
 * 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 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 to null.
	 *
	 * @param useDefaults If true, all color values will
	 *        be set to default colors; if false, all colors
	 *        will be initially null.
	 */
	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 if this color scheme and
	 *         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; inull, 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; i keep 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; inull, 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 return new 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: *
    *
  • 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 either t or f. *
    • style is the family,style,size * triplet described above. *
    *
* * @return A string representing the rgb values of the colors. */ public String toCommaSeparatedString() { StringBuffer sb = new StringBuffer(VERSION); sb.append(','); for (int i=0; iSyntaxScheme 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; } } } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy