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

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

/*
 * 08/06/2004
 *
 * RSyntaxUtilities.java - Utility methods used by RSyntaxTextArea and its
 * views.
 *
 * This library is distributed under a modified BSD license.  See the included
 * LICENSE file for details.
 */
package org.fife.ui.rsyntaxtextarea;

import java.awt.Color;
import java.awt.Container;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JViewport;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.Position;
import javax.swing.text.Segment;
import javax.swing.text.TabExpander;
import javax.swing.text.View;

import org.fife.ui.rsyntaxtextarea.TokenUtils.TokenSubList;
import org.fife.ui.rsyntaxtextarea.folding.FoldManager;
import org.fife.ui.rtextarea.Gutter;
import org.fife.ui.rtextarea.RTextArea;
import org.fife.ui.rtextarea.RTextScrollPane;


/**
 * Utility methods used by RSyntaxTextArea and its associated
 * classes.
 *
 * @author Robert Futrell
 * @version 0.2
 */
public final class RSyntaxUtilities implements SwingConstants {

	/**
	 * Integer constant representing a Windows-variant OS.
	 */
	public static final int OS_WINDOWS			= 1;

	/**
	 * Integer constant representing Mac OS X.
	 */
	public static final int OS_MAC_OSX			= 2;

	/**
	 * Integer constant representing Linux.
	 */
	public static final int OS_LINUX			= 4;

	/**
	 * Integer constant representing an "unknown" OS.  99.99% of the
	 * time, this means some UNIX variant (AIX, SunOS, etc.).
	 */
	public static final int OS_OTHER			= 8;

	/**
	 * Used for the color of hyperlinks when a LookAndFeel uses light text
	 * against a dark background.
	 */
	private static final Color LIGHT_HYPERLINK_FG = new Color(0xd8ffff);

	private static final int OS = getOSImpl();

	//private static final int DIGIT_MASK			= 1;
	private static final int LETTER_MASK			= 2;
	//private static final int WHITESPACE_MASK		= 4;
	//private static final int UPPER_CASE_MASK		= 8;
	private static final int HEX_CHARACTER_MASK		= 16;
	private static final int LETTER_OR_DIGIT_MASK	= 32;
	private static final int BRACKET_MASK			= 64;
	private static final int JAVA_OPERATOR_MASK		= 128;

	/**
	 * A lookup table used to quickly decide if a 16-bit Java char is a
	 * US-ASCII letter (A-Z or a-z), a digit, a whitespace char (either space
	 * (0x0020) or tab (0x0009)), etc.  This method should be faster
	 * than Character.isLetter, Character.isDigit,
	 * and Character.isWhitespace because we know we are dealing
	 * with ASCII chars and so don't have to worry about code planes, etc.
	 */
	private static final int[] DATA_TABLE = {
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   4,   0,   0,   0,   0,   0,   0, // 0-15
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 16-31
		 4, 128,   0,   0,   0, 128, 128,   0,  64,  64, 128, 128,   0, 128,   0, 128, // 32-47
		49,  49,  49,  49,  49,  49,  49,  49,  49,  49, 128,   0, 128, 128, 128, 128, // 48-63
		 0,  58,  58,  58,  58,  58,  58,  42,  42,  42,  42,  42,  42,  42,  42,  42, // 64-79
		42,  42,  42,  42,  42,  42,  42,  42,  42,  42,  42,  64,   0,  64, 128,   0, // 80-95
		 0,  50,  50,  50,  50,  50,  50,  34,  34,  34,  34,  34,  34,  34,  34,  34, // 96-111
		34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  34,  64, 128,  64, 128,   0, // 112-127
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 128-143
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 144-
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 160-
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 176-
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 192-
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 208-
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // 224-
		 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0  // 240-255.
	};

	/**
	 * Used in bracket matching methods.
	 */
	private static Segment charSegment = new Segment();

	/**
	 * Used in token list manipulation methods.
	 */
	private static final TokenImpl TEMP_TOKEN = new TokenImpl();

	/**
	 * Used internally.
	 */
	private static final char[] JS_KEYWORD_RETURN = { 'r', 'e', 't', 'u', 'r', 'n' };
	private static final char[] JS_AND = { '&', '&' };
	private static final char[] JS_OR  = { '|', '|' };

	/**
	 * Used internally.
	 */
	private static final String BRACKETS = "{([})]";


	/**
	 * An unused constructor to prevent instantiation, and keep static analysis
	 * tools happy.
	 */
	private RSyntaxUtilities() { // NOSONAR
		// Private constructor to prevent instantiation.
	}


	/**
	 * Returns a string with characters that are special to HTML (such as
	 * <, > and &) replaced
	 * by their HTML escape sequences.
	 *
	 * @param s The input string.
	 * @param newlineReplacement What to replace newline characters with.
	 *        If this is null, they are simply removed.
	 * @param inPreBlock Whether this HTML will be in within pre
	 *        tags.  If this is true, spaces will be kept as-is;
	 *        otherwise, they will be converted to " ".
	 * @return The escaped version of s.
	 */
	public static String escapeForHtml(String s,
						String newlineReplacement, boolean inPreBlock) {

		if (s==null) {
			return null;
		}
		if (newlineReplacement==null) {
			newlineReplacement = "";
		}
		final String tabString = "   ";
		boolean lastWasSpace = false;

		StringBuilder sb = new StringBuilder();

		for (int i=0; i':
					sb.append(">");
					lastWasSpace = false;
					break;
				case '\'':
					sb.append("'");
					lastWasSpace = false;
					break;
				case '"':
					sb.append(""");
					lastWasSpace = false;
					break;
				case '/': // OWASP-recommended even though unnecessary
					sb.append("/");
					lastWasSpace = false;
					break;
				default:
					sb.append(ch);
					lastWasSpace = false;
					break;
			}
		}

		return sb.toString();

	}


	/**
	 * Returns the rendering hints for text that will most accurately reflect
	 * those of the native windowing system.
	 *
	 * @return The rendering hints, or null if they cannot be
	 *         determined.
	 */
	public static Map getDesktopAntiAliasHints() {
		return (Map)Toolkit.getDefaultToolkit().
				getDesktopProperty("awt.font.desktophints");
	}


	/**
	 * Returns the color to use for the line underneath a folded region line.
	 *
	 * @param textArea The text area.
	 * @return The color to use.
	 */
	public static Color getFoldedLineBottomColor(RSyntaxTextArea textArea) {
		Color color = Color.GRAY;
		Gutter gutter = RSyntaxUtilities.getGutter(textArea);
		if (gutter!=null) {
			color = gutter.getFoldIndicatorForeground();
		}
		return color;
	}


	/**
	 * Returns the gutter component of the scroll pane containing a text
	 * area, if any.
	 *
	 * @param textArea The text area.
	 * @return The gutter, or null if the text area is not in
	 *         an {@link RTextScrollPane}.
	 * @see RTextScrollPane#getGutter()
	 */
	public static Gutter getGutter(RTextArea textArea) {
		Gutter gutter = null;
		Container parent = textArea.getParent();
		if (parent instanceof JViewport) {
			parent = parent.getParent();
			if (parent instanceof RTextScrollPane) {
				RTextScrollPane sp = (RTextScrollPane)parent;
				gutter = sp.getGutter(); // Should always be non-null
			}
		}
		return gutter;
	}


	/**
	 * Returns the color to use for hyperlink-style components.  This method
	 * will return Color.blue unless it appears that the current
	 * LookAndFeel uses light text on a dark background, in which case a
	 * brighter alternative is returned.
	 *
	 * @return The color to use for hyperlinks.
	 * @see #isLightForeground(Color)
	 */
	public static Color getHyperlinkForeground() {

		// This property is defined by all standard LaFs, even Nimbus (!),
		// but you never know what crazy LaFs there are...
		Color fg = UIManager.getColor("Label.foreground");
		if (fg==null) {
			fg = new JLabel().getForeground();
		}

		return isLightForeground(fg) ? LIGHT_HYPERLINK_FG : Color.blue;

	}


	/**
	 * Returns the leading whitespace of a string.
	 *
	 * @param text The String to check.
	 * @return The leading whitespace.
	 * @see #getLeadingWhitespace(Document, int)
	 */
	public static String getLeadingWhitespace(String text) {
		int count = 0;
		int len = text.length();
		while (countoffs is not a valid offset
	 *         in the document.
	 * @see #getLeadingWhitespace(String)
	 */
	public static String getLeadingWhitespace(Document doc, int offs)
									throws BadLocationException {
		Element root = doc.getDefaultRootElement();
		int line = root.getElementIndex(offs);
		Element elem = root.getElement(line);
		int startOffs = elem.getStartOffset();
		int endOffs = elem.getEndOffset() - 1;
		String text = doc.getText(startOffs, endOffs-startOffs);
		return getLeadingWhitespace(text);
	}


	private static Element getLineElem(Document doc, int offs) {
		Element root = doc.getDefaultRootElement();
		int line = root.getElementIndex(offs);
		Element elem = root.getElement(line);
		if ((offs>=elem.getStartOffset()) && (offsp0, as this is
	 * the character where the x-pixel value is 0.
	 *
	 * @param textArea The text area containing the text.
	 * @param s A segment in which to load the line.  This is passed in so we
	 *        don't have to reallocate a new Segment for each
	 *        call.
	 * @param p0 The starting position in the physical line in the document.
	 * @param p1 The position for which to get the bounding box in the view.
	 * @param e How to expand tabs.
	 * @param rect The rectangle whose x- and width-values are changed to
	 *        represent the bounding box of p1.  This is reused
	 *        to keep from needlessly reallocating Rectangles.
	 * @param x0 The x-coordinate (pixel) marking the left-hand border of the
	 *        text.  This is useful if the text area has a border, for example.
	 * @return The bounding box in the view of the character p1.
	 * @throws BadLocationException If p0 or p1 is
	 *         not a valid location in the specified text area's document.
	 * @throws IllegalArgumentException If p0 and p1
	 *         are not on the same line.
	 */
	public static Rectangle getLineWidthUpTo(RSyntaxTextArea textArea,
								Segment s, int p0, int p1,
								TabExpander e, Rectangle rect,
								int x0)
								throws BadLocationException {

		RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();

		// Ensure p0 and p1 are valid document positions.
		if (p0<0) {
			throw new BadLocationException("Invalid document position", p0);
		}
		else if (p1>doc.getLength()) {
			throw new BadLocationException("Invalid document position", p1);
		}

		// Ensure p0 and p1 are in the same line, and get the start/end
		// offsets for that line.
		Element map = doc.getDefaultRootElement();
		int lineNum = map.getElementIndex(p0);
		// We do ">1" because p1 might be the first position on the next line
		// or the last position on the previous one.
		// if (lineNum!=map.getElementIndex(p1))
		if (Math.abs(lineNum-map.getElementIndex(p1))>1) {
			throw new IllegalArgumentException("p0 and p1 are not on the " +
						"same line (" + p0 + ", " + p1 + ").");
		}

		// Get the token list.
		Token t = doc.getTokenListForLine(lineNum);

		// Modify the token list 't' to begin at p0 (but still have correct
		// token types, etc.), and get the x-location (in pixels) of the
		// beginning of this new token list.
		TokenSubList subList = TokenUtils.getSubTokenList(t, p0, e, textArea,
				0, TEMP_TOKEN);
		t = subList.tokenList;

		rect = t.listOffsetToView(textArea, e, p1, x0, rect);
		return rect;

	}


	/**
	 * Returns the location of the bracket paired with the one at the current
	 * caret position.
	 *
	 * @param textArea The text area.
	 * @param input A point to use as the return value.  If this is
	 *        null, a new object is created and returned.
	 * @return A point representing the matched bracket info.  The "x" field
	 *         is the offset of the bracket at the caret position (either just
	 *         before or just after the caret), and the "y" field is the offset
	 *         of the matched bracket.  Both "x" and "y" will be
	 *         -1 if there isn't a matching bracket (or the caret
	 *         isn't on a bracket).
	 */
	public static Point getMatchingBracketPosition(RSyntaxTextArea textArea,
										Point input) {

		if (input==null) {
			input = new Point();
		}
		input.setLocation(-1, -1);

		try {

			// Actually position just BEFORE caret.
			int caretPosition = textArea.getCaretPosition() - 1;
			RSyntaxDocument doc = (RSyntaxDocument)textArea.getDocument();
			char bracket = 0;

			// If the caret was at offset 0, we can't check "to its left."
			if (caretPosition>=0) {
				bracket  = doc.charAt(caretPosition);
			}

			// Try to match a bracket "to the right" of the caret if one
			// was not found on the left.
			int index = BRACKETS.indexOf(bracket);
			if (index==-1 && caretPosition




© 2015 - 2024 Weber Informatics LLC | Privacy Policy