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

org.fife.rsta.ac.java.JavaLinkGenerator Maven / Gradle / Ivy

Go to download

A library adding code completion and other advanced features for Java, JavaScript, Perl, and other languages to RSyntaxTextArea.

There is a newer version: 3.3.0
Show newest version
/*
 * 02/17/2013
 *
 * Copyright (C) 2013 Robert Futrell
 * robert_futrell at users.sourceforge.net
 * http://fifesoft.com/rsyntaxtextarea
 *
 * This library is distributed under a modified BSD license.  See the included
 * RSTALanguageSupport.License.txt file for details.
 */
package org.fife.rsta.ac.java;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.swing.text.BadLocationException;

import org.fife.rsta.ac.java.rjc.ast.CodeBlock;
import org.fife.rsta.ac.java.rjc.ast.CompilationUnit;
import org.fife.rsta.ac.java.rjc.ast.FormalParameter;
import org.fife.rsta.ac.java.rjc.ast.LocalVariable;
import org.fife.rsta.ac.java.rjc.ast.Member;
import org.fife.rsta.ac.java.rjc.ast.Method;
import org.fife.rsta.ac.java.rjc.ast.TypeDeclaration;
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
import org.fife.ui.rsyntaxtextarea.SelectRegionLinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.Token;


/**
 * Checks for hyperlink-able tokens under the mouse position when Ctrl is
 * pressed (Cmd on OS X).  Currently this class only checks for accessible
 * members in the current file only (e.g. no members in super classes, no other
 * classes on the classpath, etc.).  So naturally, there is a lot of room for
 * improvement. IDE-style applications, for example, would want to check
 * for members in super-classes, and open their source on click events.
 * 
 * @author Robert Futrell
 * @version 1.0
 */
// TODO: Anonymous inner classes probably aren't handled well.
class JavaLinkGenerator implements LinkGenerator {

	private JavaLanguageSupport jls;


	JavaLinkGenerator(JavaLanguageSupport jls) {
		this.jls = jls;
	}


	/**
	 * Checks if the token at the specified offset is possibly a "click-able"
	 * region.
	 *
	 * @param textArea The text area.
	 * @param offs The offset, presumably at the mouse position.
	 * @return A result object.
	 */
	private IsLinkableCheckResult checkForLinkableToken(
			RSyntaxTextArea textArea, int offs) {

		IsLinkableCheckResult result = null;

		if (offs>=0) {

			try {

				int line = textArea.getLineOfOffset(offs);
				Token first = textArea.getTokenListForLine(line);
				Token prev = null;

				for (Token t=first; t!=null && t.isPaintable(); t=t.getNextToken()) {

					if (t.containsPosition(offs)) {

						// RSTA's tokens are pooled and re-used, so we must
						// defensively make a copy of the one we want to keep!
						Token token = new Token(t);
						boolean isMethod = false;

						if (prev==null) {
							prev = RSyntaxUtilities.getPreviousImportantToken(
									textArea, line-1);
						}
						if (prev!=null && prev.isSingleChar('.')) {
							// Not a field or method defined in this class.
							break;
						}

						Token next = RSyntaxUtilities.getNextImportantToken(
								t.getNextToken(), textArea, line);
						if (next!=null && next.isSingleChar(Token.SEPARATOR, '(')) {
							isMethod = true;
						}

						result = new IsLinkableCheckResult(token, isMethod);
						break;

					}

					else if (!t.isCommentOrWhitespace()) {
						prev = t;
					}

				}

			} catch (BadLocationException ble) {
				ble.printStackTrace(); // Never happens
			}

		}

		return result;

	}


	/**
	 * {@inheritDoc}
	 */
	public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea,
			int offs) {

		int start = -1;
		int end = -1;

		IsLinkableCheckResult result = checkForLinkableToken(textArea, offs);
		if (result!=null) {

			JavaParser parser = jls.getParser(textArea);
			CompilationUnit cu = parser.getCompilationUnit();
			Token t = result.token;
			boolean method = result.method;

			if (cu!=null) {

				TypeDeclaration td = cu.getDeepestTypeDeclarationAtOffset(offs);
				boolean staticFieldsOnly = false;
				boolean deepestTypeDec = true;
				boolean deepestContainingMemberStatic = false;
				while (td!=null && start==-1) {

					// First, check for a local variable in methods/static blocks
					if (!method && deepestTypeDec) {

						for (Iterator i=td.getMemberIterator(); i.hasNext(); ) {
	
							Method m = null; // Nasty!  Clean this code up
							Member member = (Member)i.next();
							CodeBlock block = null;

							// Check if a method or static block contains offs
							if (member instanceof Method) {
								m = (Method)member;
								if (m.getBodyContainsOffset(offs) && m.getBody()!=null) {
									deepestContainingMemberStatic = m.isStatic();
									block = m.getBody().getDeepestCodeBlockContaining(offs);
								}
							}
							else if (member instanceof CodeBlock) {
								block = (CodeBlock)member;
								deepestContainingMemberStatic = block.isStatic();
								block = block.getDeepestCodeBlockContaining(offs);
							}

							// If so, scan its locals
							if (block!=null) {
								String varName = t.getLexeme();
								// Local variables first, in reverse order
								List locals = block.getLocalVarsBefore(offs);
								Collections.reverse(locals);
								for (Iterator j=locals.iterator(); j.hasNext(); ) {
									LocalVariable local = (LocalVariable)j.next();
									if (varName.equals(local.getName())) {
										start = local.getNameStartOffset();
										end = local.getNameEndOffset();
									}
								}
								// Then arguments, if any.
								if (start==-1 && m!=null) {
									for (int j=0; j-1) {
				return new SelectRegionLinkGeneratorResult(textArea, t.offset,
						start, end);
			}

		}

		return null;

	}


	/**
	 * The result of checking whether a region of code under the mouse is
	 * possibly link-able.
	 */
	private static class IsLinkableCheckResult {

		/**
		 * The token under the mouse position.
		 */
		private Token token;

		/**
		 * Whether the token is a method invocation (as opposed to a local
		 * variable or field).
		 */
		private boolean method;

		private IsLinkableCheckResult(Token token, boolean method) {
			this.token = token;
			this.method = method;
		}

	}


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy