Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 03/21/2010
*
* Copyright (C) 2010 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This code is licensed under the LGPL. See the "license.txt" file included
* with this project.
*/
package org.fife.rsta.ac.java;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.swing.text.JTextComponent;
import org.fife.rsta.ac.java.classreader.ClassFile;
import org.fife.rsta.ac.java.classreader.FieldInfo;
import org.fife.rsta.ac.java.classreader.MemberInfo;
import org.fife.rsta.ac.java.classreader.MethodInfo;
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.Field;
import org.fife.rsta.ac.java.rjc.ast.FormalParameter;
import org.fife.rsta.ac.java.rjc.ast.ImportDeclaration;
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.NormalClassDeclaration;
import org.fife.rsta.ac.java.rjc.ast.TypeDeclaration;
import org.fife.rsta.ac.java.rjc.lang.Type;
import org.fife.ui.autocomplete.DefaultCompletionProvider;
/**
* Parses a Java AST for code completions. It currently scans the following:
*
*
*
Import statements
*
Method names
*
Field names
*
*
* Also, if the caret is inside a method, local variables up to the caret
* position are also returned.
*
* @author Robert Futrell
* @version 1.0
*/
class SourceCompletionProvider extends DefaultCompletionProvider {
/**
* The parent completion provider.
*/
private JavaCompletionProvider javaProvider;
/**
* Used to get information about what classes match imports.
*/
private JarManager jarManager;
private static final String JAVA_LANG_PACKAGE = "java.lang.*";
private static final String THIS = "this";
/**
* Constructor.
*/
public SourceCompletionProvider() {
this(null);
}
/**
* Constructor.
*
* @param jarManager The jar manager for this provider.
*/
public SourceCompletionProvider(JarManager jarManager) {
if (jarManager==null) {
jarManager = new JarManager();
}
this.jarManager = jarManager;
setParameterizedCompletionParams('(', ", ", ')');
setAutoActivationRules(false, "."); // Default - only activate after '.'
}
private void addCompletionsForStaticMembers(Set set,
CompilationUnit cu, ClassFile cf, String pkg) {
// Check us first, so if we override anything, we get the "newest"
// version.
int methodCount = cf.getMethodCount();
for (int i=0; inull if
* cf represents java.lang.Object (or
* if the super class could not be determined).
*/
private ClassFile getClassFileFor(CompilationUnit cu, String pkg,
String className) {
//System.err.println(">>> Getting class file for: " + className);
if (className==null) {
return null;
}
ClassFile superClass = null;
// Determine the fully qualified class to grab
if (!Util.isFullyQualified(className)) {
// Check in this source file's package first
if (pkg!=null) {
String temp = pkg + "." + className;
superClass = jarManager.getClassEntry(temp);
}
// Next, go through the imports (order is important)
if (superClass==null) {
for (Iterator i=cu.getImportIterator(); i.hasNext(); ) {
ImportDeclaration id = (ImportDeclaration)i.next();
String imported = id.getName();
if (imported.endsWith(".*")) {
String temp = imported.substring(
0, imported.length()-1) + className;
superClass = jarManager.getClassEntry(temp);
if (superClass!=null) {
break;
}
}
else if (imported.endsWith("." + className)) {
superClass = jarManager.getClassEntry(imported);
break;
}
}
}
// Finally, try java.lang
if (superClass==null) {
String temp = "java.lang." + className;
superClass = jarManager.getClassEntry(temp);
}
}
else {
superClass = jarManager.getClassEntry(className);
}
return superClass;
}
/**
* Adds completions for local variables in a method.
*
* @param set
* @param method
* @param offs The caret's offset into the source. This should be inside
* of method.
*/
private void addLocalVarCompletions(Set set, Method method, int offs) {
for (int i=0; iblock.
*/
private void addLocalVarCompletions(Set set, CodeBlock block, int offs) {
for (int i=0; ioffs) {
break;
}
}
}
/**
* Adds a jar to read from.
*
* @param info The jar to add. If this is null, then
* the current JVM's main JRE jar (rt.jar, or classes.jar on OS X)
* will be added. If this jar has already been added, adding it
* again will do nothing (except possibly update its attached source
* location).
* @throws IOException If an IO error occurs.
* @see #getJars()
* @see #removeJar(File)
*/
public void addJar(JarInfo info) throws IOException {
jarManager.addJar(info);
}
/**
* Removes all jars from the "build path."
*
* @see #removeJar(File)
* @see #addJar(JarInfo)
* @see #getJars()
*/
public void clearJars() {
jarManager.clearJars();
// The memory used by the completions can be quite large, so go ahead
// and clear out the completions list so no-longer-needed ones are
// eligible for GC.
clear();
}
/**
* {@inheritDoc}
*/
protected List getCompletionsImpl(JTextComponent comp) {
comp.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.WAIT_CURSOR));
try {
completions.clear();
CompilationUnit cu = javaProvider.getCompilationUnit();
if (cu==null) {
return completions; // empty
}
Set set = new TreeSet();
// Cut down the list to just those matching what we've typed.
String text = getAlreadyEnteredText(comp);
// Don't add shorthand completions if they're typing something qualified
if (text.indexOf('.')==-1) {
addShorthandCompletions(set);
}
loadImportCompletions(set, text, cu);
// Add completions for fully-qualified stuff (e.g. "com.sun.jav")
//long startTime = System.currentTimeMillis();
jarManager.addCompletions(this, text, set);
//long time = System.currentTimeMillis() - startTime;
//System.out.println("jar completions loaded in: " + time);
// Loop through all types declared in this source, and provide
// completions depending on in what type, method, etc. the caret is in.
loadCompletionsForCaretPosition(cu, comp, text, set);
// Do a final sort of all of our completions and we're good to go!
completions = new ArrayList(set);
Collections.sort(completions);
// Only match based on stuff after the final '.', since that's what is
// displayed for all of our completions.
text = text.substring(text.lastIndexOf('.')+1);
int start = Collections.binarySearch(completions, text, comparator);
if (start<0) {
start = -(start+1);
}
else {
// There might be multiple entries with the same input text.
while (start>0 &&
comparator.compare(completions.get(start-1), text)==0) {
start--;
}
}
int end = Collections.binarySearch(completions, text+'{', comparator);
end = -(end+1);
return completions.subList(start, end);
} finally {
comp.setCursor(java.awt.Cursor.getPredefinedCursor(java.awt.Cursor.TEXT_CURSOR));
}
}
/**
* Returns the jars on the "build path."
*
* @return A list of {@link JarInfo}s. Modifying a JarInfo in
* this list will have no effect on this completion provider; in
* order to do that, you must re-add the jar via
* {@link #addJar(JarInfo)}. If there are no jars on the
* "build path," this will be an empty list.
* @see #addJar(JarInfo)
*/
public List getJars() {
return jarManager.getJars();
}
public File getSourceLocForClass(String className) {
return jarManager.getSourceLocForClass(className);
}
/**
* Returns whether a method defined by a super class is accessible to
* this class.
*
* @param info Information about the member.
* @param pkg The package of the source currently being parsed.
* @return Whether or not the method is accessible.
*/
private boolean isAccessible(MemberInfo info, String pkg) {
boolean accessible = false;
int access = info.getAccessFlags();
if (org.fife.rsta.ac.java.classreader.Util.isPublic(access) ||
org.fife.rsta.ac.java.classreader.Util.isProtected(access)) {
accessible = true;
}
else if (org.fife.rsta.ac.java.classreader.Util.isDefault(access)) {
String pkg2 = info.getClassFile().getPackageName();
accessible = (pkg==null && pkg2==null) ||
(pkg!=null && pkg.equals(pkg2));
}
return accessible;
}
/**
* {@inheritDoc}
*/
protected boolean isValidChar(char ch) {
return Character.isJavaIdentifierPart(ch) || ch=='.';
}
/**
* Loads completions based on the current caret location in the source. In
* other words:
*
*
*
If the caret is anywhere in a class, the names of all methods and
* fields in the class are loaded. Methods and fields in super
* classes are also loaded. TODO: Get super methods/fields added
* correctly by access!
*
If the caret is in a field, local variables currently accessible
* are loaded.
*
*
* @param cu
* @param comp
* @param alreadyEntered
* @param retVal
*/
private void loadCompletionsForCaretPosition(CompilationUnit cu,
JTextComponent comp, String alreadyEntered, Set retVal) {
// Get completions for all fields and methods of all type declarations.
//long startTime = System.currentTimeMillis();
int caret = comp.getCaretPosition();
//List temp = new ArrayList();
int start, end;
int lastDot = alreadyEntered.lastIndexOf('.');
boolean qualified = lastDot>-1;
String prefix = qualified ? alreadyEntered.substring(0, lastDot) : null;
for (Iterator i=cu.getTypeDeclarationIterator(); i.hasNext(); ) {
TypeDeclaration td = (TypeDeclaration)i.next();
start = td.getBodyStartOffset();
end = td.getBodyEndOffset();
if (caret>start && caret<=end) {
loadCompletionsForCaretPosition(cu, comp, alreadyEntered,
retVal, td, prefix, caret);
}
else if (caret
*
If the caret is anywhere in a class, the names of all methods and
* fields in the class are loaded. Methods and fields in super
* classes are also loaded. TODO: Get super methods/fields added
* correctly by access!
*
If the caret is in a field, local variables currently accessible
* are loaded.
*
*
* @param cu
* @param comp
* @param alreadyEntered
* @param retVal
*/
private void loadCompletionsForCaretPosition(CompilationUnit cu,
JTextComponent comp, String alreadyEntered, Set retVal,
TypeDeclaration td, String prefix, int caret) {
// Do any child types first, so if any vars, etc. have duplicate names,
// we pick up the one "closest" to us first.
for (int i=0; i=method.getBodyStartOffset() && caretnull if
* none.
* @param prefix The text up to the current caret position. This is
* guaranteed to be non-null not equal to
* "this".
* @param offs The offset of the caret in the document.
*/
private void loadCompletionsForCaretPositionQualified(CompilationUnit cu,
String alreadyEntered, Set retVal,
TypeDeclaration td, Method currentMethod, String prefix, int offs) {
// TODO: Remove this restriction.
int dot = prefix.indexOf('.');
if (dot>-1) {
System.out.println("[DEBUG]: Qualified non-this completions currently only go 1 level deep");
return;
}
// TODO: Remove this restriction.
else if (!prefix.matches("[A-Za-z_][A-Za-z0-9_\\$]*")) {
System.out.println("[DEBUG]: Only identifier non-this completions are currently supported");
return;
}
String pkg = cu.getPackageName();
boolean matched = false;
for (Iterator j=td.getMemberIterator(); j.hasNext(); ) {
Member m = (Member)j.next();
// The prefix might be a field in the local class.
if (m instanceof Field) {
Field field = (Field)m;
if (field.getName().equals(prefix)) {
System.out.println("FOUND: " + prefix + " (" + pkg + ")");
Type type = field.getType();
if (type.isArray()) {
ClassFile cf = getClassFileFor(cu, pkg, "java.lang.Object");
addCompletionsForExtendedClass(retVal, cu, cf, pkg);
FieldCompletion fc = FieldCompletion.
createLengthCompletion(this, type.toString());
retVal.add(fc);
}
else if (!type.isBasicType()) {
String typeStr = type.toString();
ClassFile cf = getClassFileFor(cu, pkg, typeStr);
if (cf!=null) {
addCompletionsForExtendedClass(retVal, cu, cf, pkg);
}
}
matched = true;
break;
}
}
}
// The prefix might be for a local variable in the current method.
if (currentMethod!=null) {
boolean found = false;
// Check parameters to the current method
for (int i=0; ioffs) {
break;
}
}
}
/**
* Loads completions for a single import statement.
*
* @param importStr The import statement.
* @param pkgName The package of the source currently being parsed.
*/
private void loadCompletionsForImport(Set set, String importStr, String pkgName) {
if (importStr.endsWith(".*")) {
String pkg = importStr.substring(0, importStr.length()-2);
boolean inPkg = pkg.equals(pkgName);
List classes = jarManager.getClassesInPackage(pkg, inPkg);
for (Iterator i=classes.iterator(); i.hasNext(); ) {
ClassFile cf = (ClassFile)i.next();
set.add(new ClassCompletion(this, cf));
}
}
else {
ClassFile cf = jarManager.getClassEntry(importStr);
if (cf!=null) {
set.add(new ClassCompletion(this, cf));
}
}
}
/**
* Loads completions for all import statements.
*
* @param cu The compilation unit being parsed.
*/
private void loadImportCompletions(Set set, String text,
CompilationUnit cu) {
// Fully-qualified completions are handled elsewhere, so no need to
// duplicate the work here
if (text.indexOf('.')>-1) {
return;
}
//long startTime = System.currentTimeMillis();
String pkgName = cu.getPackageName();
loadCompletionsForImport(set, JAVA_LANG_PACKAGE, pkgName);
for (Iterator i=cu.getImportIterator(); i.hasNext(); ) {
ImportDeclaration id = (ImportDeclaration)i.next();
String name = id.getName();
if (!JAVA_LANG_PACKAGE.equals(name)) {
loadCompletionsForImport(set, name, pkgName);
}
}
// Collections.sort(completions);
//long time = System.currentTimeMillis() - startTime;
//System.out.println("imports loaded in: " + time);
}
/**
* Removes a jar from the "build path."
*
* @param jar The jar to remove.
* @return Whether the jar was removed. This will be false
* if the jar was not on the build path.
* @see #addJar(JarInfo)
* @see #getJars()
* @see #clearJars()
*/
public boolean removeJar(File jar) {
boolean removed = jarManager.removeJar(jar);
// The memory used by the completions can be quite large, so go ahead
// and clear out the completions list so no-longer-needed ones are
// eligible for GC.
if (removed) {
clear();
}
return removed;
}
/**
* Sets the parent Java provider.
*
* @param javaProvider The parent completion provider.
*/
void setJavaProvider(JavaCompletionProvider javaProvider) {
this.javaProvider = javaProvider;
}
}