mirror of
https://github.com/processing/processing4.git
synced 2026-01-30 20:01:09 +01:00
2086 lines
79 KiB
Java
2086 lines
79 KiB
Java
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
|
|
|
/*
|
|
Part of the Processing project - http://processing.org
|
|
Copyright (c) 2012-15 The Processing Foundation
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2
|
|
as published by the Free Software Foundation.
|
|
|
|
This program 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation, Inc.
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
package processing.mode.java;
|
|
|
|
import java.lang.reflect.Constructor;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
import java.util.regex.Pattern;
|
|
import java.util.stream.Stream;
|
|
|
|
import javax.swing.DefaultListModel;
|
|
|
|
import org.eclipse.jdt.core.dom.AST;
|
|
import org.eclipse.jdt.core.dom.ASTNode;
|
|
import org.eclipse.jdt.core.dom.ASTParser;
|
|
import org.eclipse.jdt.core.dom.ArrayAccess;
|
|
import org.eclipse.jdt.core.dom.ArrayType;
|
|
import org.eclipse.jdt.core.dom.Block;
|
|
import org.eclipse.jdt.core.dom.CompilationUnit;
|
|
import org.eclipse.jdt.core.dom.Expression;
|
|
import org.eclipse.jdt.core.dom.ExpressionStatement;
|
|
import org.eclipse.jdt.core.dom.FieldAccess;
|
|
import org.eclipse.jdt.core.dom.FieldDeclaration;
|
|
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
|
import org.eclipse.jdt.core.dom.MethodInvocation;
|
|
import org.eclipse.jdt.core.dom.Name;
|
|
import org.eclipse.jdt.core.dom.ParameterizedType;
|
|
import org.eclipse.jdt.core.dom.PrimitiveType;
|
|
import org.eclipse.jdt.core.dom.QualifiedName;
|
|
import org.eclipse.jdt.core.dom.SimpleName;
|
|
import org.eclipse.jdt.core.dom.SimpleType;
|
|
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
|
|
import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
|
|
import org.eclipse.jdt.core.dom.Type;
|
|
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
|
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
|
|
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
|
|
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
|
|
|
|
import processing.app.Messages;
|
|
|
|
import com.google.classpath.ClassPath;
|
|
import com.google.classpath.RegExpResourceFilter;
|
|
|
|
@SuppressWarnings({ "unchecked" })
|
|
public class CompletionGenerator {
|
|
|
|
public CompletionGenerator() {
|
|
//addCompletionPopupListner();
|
|
//loadJavaDoc();
|
|
}
|
|
|
|
|
|
public static CompletionCandidate[] checkForTypes(ASTNode node) {
|
|
|
|
List<VariableDeclarationFragment> vdfs = null;
|
|
switch (node.getNodeType()) {
|
|
case ASTNode.TYPE_DECLARATION:
|
|
return new CompletionCandidate[]{new CompletionCandidate((TypeDeclaration) node)};
|
|
|
|
case ASTNode.METHOD_DECLARATION:
|
|
MethodDeclaration md = (MethodDeclaration) node;
|
|
log(getNodeAsString(md));
|
|
List<ASTNode> params = (List<ASTNode>) md
|
|
.getStructuralProperty(MethodDeclaration.PARAMETERS_PROPERTY);
|
|
CompletionCandidate[] cand = new CompletionCandidate[params.size() + 1];
|
|
cand[0] = new CompletionCandidate(md);
|
|
for (int i = 0; i < params.size(); i++) {
|
|
// cand[i + 1] = new CompletionCandidate(params.get(i).toString(), "", "",
|
|
// CompletionCandidate.LOCAL_VAR);
|
|
cand[i + 1] = new CompletionCandidate((SingleVariableDeclaration) params.get(i));
|
|
}
|
|
return cand;
|
|
|
|
case ASTNode.SINGLE_VARIABLE_DECLARATION:
|
|
return new CompletionCandidate[]{new CompletionCandidate((SingleVariableDeclaration) node)};
|
|
|
|
case ASTNode.FIELD_DECLARATION:
|
|
vdfs = ((FieldDeclaration) node).fragments();
|
|
break;
|
|
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
|
|
vdfs = ((VariableDeclarationStatement) node).fragments();
|
|
break;
|
|
case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
|
|
vdfs = ((VariableDeclarationExpression) node).fragments();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (vdfs != null) {
|
|
CompletionCandidate ret[] = new CompletionCandidate[vdfs.size()];
|
|
int i = 0;
|
|
for (VariableDeclarationFragment vdf : vdfs) {
|
|
// ret[i++] = new CompletionCandidate(getNodeAsString2(vdf), "", "",
|
|
// CompletionCandidate.LOCAL_VAR);
|
|
ret[i++] = new CompletionCandidate(vdf);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Find the parent of the expression in a().b, this would give me the return
|
|
* type of a(), so that we can find all children of a() begininng with b
|
|
*
|
|
* @param nearestNode
|
|
* @param expression
|
|
* @return
|
|
*/
|
|
public static ASTNode resolveExpression(ASTNode nearestNode,
|
|
ASTNode expression, boolean noCompare) {
|
|
log("Resolving " + getNodeAsString(expression) + " noComp "
|
|
+ noCompare);
|
|
if (expression instanceof SimpleName) {
|
|
return findDeclaration2(((SimpleName) expression), nearestNode);
|
|
} else if (expression instanceof MethodInvocation) {
|
|
log("3. Method Invo "
|
|
+ ((MethodInvocation) expression).getName());
|
|
return findDeclaration2(((MethodInvocation) expression).getName(),
|
|
nearestNode);
|
|
} else if (expression instanceof FieldAccess) {
|
|
log("2. Field access "
|
|
+ getNodeAsString(((FieldAccess) expression).getExpression()) + "|||"
|
|
+ getNodeAsString(((FieldAccess) expression).getName()));
|
|
if (noCompare) {
|
|
/*
|
|
* ASTNode ret = findDeclaration2(((FieldAccess) expression).getName(),
|
|
* nearestNode); log("Found as ->"+getNodeAsString(ret));
|
|
* return ret;
|
|
*/
|
|
return findDeclaration2(((FieldAccess) expression).getName(),
|
|
nearestNode);
|
|
} else {
|
|
|
|
/*
|
|
* Note how for the next recursion, noCompare is reversed. Let's say
|
|
* I've typed getABC().quark.nin where nin is incomplete(ninja being the
|
|
* field), when execution first enters here, it calls resolveExpr again
|
|
* for "getABC().quark" where we know that quark field must be complete,
|
|
* so we toggle noCompare. And kaboom.
|
|
*/
|
|
return resolveExpression(nearestNode,
|
|
((FieldAccess) expression).getExpression(),
|
|
true);
|
|
}
|
|
//return findDeclaration2(((FieldAccess) expression).getExpression(), nearestNode);
|
|
} else if (expression instanceof QualifiedName) {
|
|
log("1. Resolving "
|
|
+ ((QualifiedName) expression).getQualifier() + " ||| "
|
|
+ ((QualifiedName) expression).getName());
|
|
if (noCompare) { // no compare, as in "abc.hello." need to resolve hello here
|
|
return findDeclaration2(((QualifiedName) expression).getName(),
|
|
nearestNode);
|
|
} else {
|
|
//User typed "abc.hello.by" (bye being complete), so need to resolve "abc.hello." only
|
|
return findDeclaration2(((QualifiedName) expression).getQualifier(),
|
|
nearestNode);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Finds the type of the expression in foo.bar().a().b, this would give me the
|
|
* type of b if it exists in return type of a(). If noCompare is true,
|
|
* it'll return type of a()
|
|
* @param nearestNode
|
|
* @param astNode
|
|
* @return
|
|
*/
|
|
public static ClassMember resolveExpression3rdParty(PreprocSketch ps, ASTNode nearestNode,
|
|
ASTNode astNode, boolean noCompare) {
|
|
log("Resolve 3rdParty expr-- " + getNodeAsString(astNode)
|
|
+ " nearest node " + getNodeAsString(nearestNode));
|
|
if(astNode == null) return null;
|
|
ClassMember scopeParent;
|
|
SimpleType stp;
|
|
if(astNode instanceof SimpleName){
|
|
ASTNode decl = findDeclaration2(((SimpleName)astNode),nearestNode);
|
|
if(decl != null){
|
|
// see if locally defined
|
|
log(getNodeAsString(astNode)+" found decl -> " + getNodeAsString(decl));
|
|
|
|
{
|
|
if (decl.getNodeType() == ASTNode.TYPE_DECLARATION) {
|
|
TypeDeclaration td = (TypeDeclaration) decl;
|
|
return new ClassMember(ps, td);
|
|
}
|
|
}
|
|
|
|
{ // Handle "array." x "array[1]."
|
|
Type type = extracTypeInfo2(decl);
|
|
if (type != null && type.isArrayType() &&
|
|
astNode.getParent().getNodeType() != ASTNode.ARRAY_ACCESS) {
|
|
// No array access, we want members of the array itself
|
|
Type elementType = ((ArrayType) type).getElementType();
|
|
|
|
// Get name of the element class
|
|
String name = "";
|
|
if (elementType.isSimpleType()) {
|
|
Class<?> c = findClassIfExists(ps, elementType.toString());
|
|
if (c != null) name = c.getName();
|
|
} else if (elementType.isPrimitiveType()) {
|
|
name = ((PrimitiveType) elementType).getPrimitiveTypeCode().toString();
|
|
}
|
|
|
|
// Convert element class to array class
|
|
Class<?> arrayClass = getArrayClass(name, ps.classLoader);
|
|
|
|
return arrayClass == null ? null : new ClassMember(arrayClass);
|
|
}
|
|
}
|
|
|
|
return new ClassMember(ps, extracTypeInfo(decl));
|
|
}
|
|
else {
|
|
// or in a predefined class?
|
|
Class<?> tehClass = findClassIfExists(ps, astNode.toString());
|
|
if (tehClass != null) {
|
|
return new ClassMember(tehClass);
|
|
}
|
|
}
|
|
astNode = astNode.getParent();
|
|
}
|
|
switch (astNode.getNodeType()) {
|
|
//TODO: Notice the redundancy in the 3 cases, you can simplify things even more.
|
|
case ASTNode.FIELD_ACCESS:
|
|
FieldAccess fa = (FieldAccess) astNode;
|
|
if (fa.getExpression() == null) {
|
|
|
|
// TODO: Check for existence of 'new' keyword. Could be a ClassInstanceCreation
|
|
|
|
// Local code or belongs to super class
|
|
log("FA,Not implemented.");
|
|
return null;
|
|
} else {
|
|
if (fa.getExpression() instanceof SimpleName) {
|
|
stp = extracTypeInfo(findDeclaration2((SimpleName) fa.getExpression(),
|
|
nearestNode));
|
|
if(stp == null){
|
|
/*The type wasn't found in local code, so it might be something like
|
|
* log(), or maybe belonging to super class, etc.
|
|
*/
|
|
Class<?> tehClass = findClassIfExists(ps, fa.getExpression().toString());
|
|
if (tehClass != null) {
|
|
// Method Expression is a simple name and wasn't located locally, but found in a class
|
|
// so look for method in this class.
|
|
return definedIn3rdPartyClass(ps, new ClassMember(tehClass), fa
|
|
.getName().toString());
|
|
}
|
|
log("FA resolve 3rd par, Can't resolve " + fa.getExpression());
|
|
|
|
return null;
|
|
}
|
|
log("FA, SN Type " + getNodeAsString(stp));
|
|
scopeParent = definedIn3rdPartyClass(ps, stp.getName().toString(), "THIS");
|
|
|
|
} else {
|
|
scopeParent = resolveExpression3rdParty(ps, nearestNode,
|
|
fa.getExpression(), noCompare);
|
|
}
|
|
log("FA, ScopeParent " + scopeParent);
|
|
return definedIn3rdPartyClass(ps, scopeParent, fa.getName().toString());
|
|
}
|
|
case ASTNode.METHOD_INVOCATION:
|
|
MethodInvocation mi = (MethodInvocation) astNode;
|
|
ASTNode temp = findDeclaration2(mi.getName(), nearestNode);
|
|
if(temp instanceof MethodDeclaration){
|
|
// method is locally defined
|
|
log(mi.getName() + " was found locally," + getNodeAsString(extracTypeInfo(temp)));
|
|
|
|
{ // Handle "array." x "array[1]."
|
|
Type type = extracTypeInfo2(temp);
|
|
if (type != null && type.isArrayType() &&
|
|
astNode.getParent().getNodeType() != ASTNode.ARRAY_ACCESS) {
|
|
// No array access, we want members of the array itself
|
|
Type elementType = ((ArrayType) type).getElementType();
|
|
|
|
// Get name of the element class
|
|
String name = "";
|
|
if (elementType.isSimpleType()) {
|
|
Class<?> c = findClassIfExists(ps, elementType.toString());
|
|
if (c != null) name = c.getName();
|
|
} else if (elementType.isPrimitiveType()) {
|
|
name = ((PrimitiveType) elementType).getPrimitiveTypeCode().toString();
|
|
}
|
|
|
|
// Convert element class to array class
|
|
Class<?> arrayClass = getArrayClass(name, ps.classLoader);
|
|
|
|
return arrayClass == null ? null : new ClassMember(arrayClass);
|
|
}
|
|
}
|
|
|
|
return new ClassMember(ps, extracTypeInfo(temp));
|
|
}
|
|
if (mi.getExpression() == null) {
|
|
// if()
|
|
//Local code or belongs to super class
|
|
log("MI,Not implemented.");
|
|
return null;
|
|
} else {
|
|
if (mi.getExpression() instanceof SimpleName) {
|
|
ASTNode decl = findDeclaration2((SimpleName) mi.getExpression(),
|
|
nearestNode);
|
|
if (decl != null) {
|
|
if (decl.getNodeType() == ASTNode.TYPE_DECLARATION) {
|
|
TypeDeclaration td = (TypeDeclaration) decl;
|
|
return new ClassMember(ps, td);
|
|
}
|
|
|
|
stp = extracTypeInfo(decl);
|
|
if(stp == null){
|
|
/*The type wasn't found in local code, so it might be something like
|
|
* System.console()., or maybe belonging to super class, etc.
|
|
*/
|
|
Class<?> tehClass = findClassIfExists(ps, mi.getExpression().toString());
|
|
if (tehClass != null) {
|
|
// Method Expression is a simple name and wasn't located locally, but found in a class
|
|
// so look for method in this class.
|
|
return definedIn3rdPartyClass(ps, new ClassMember(tehClass), mi
|
|
.getName().toString());
|
|
}
|
|
log("MI resolve 3rd par, Can't resolve " + mi.getExpression());
|
|
return null;
|
|
}
|
|
log("MI, SN Type " + getNodeAsString(stp));
|
|
ASTNode typeDec = findDeclaration2(stp.getName(),nearestNode);
|
|
if(typeDec == null){
|
|
log(stp.getName() + " couldn't be found locally..");
|
|
Class<?> tehClass = findClassIfExists(ps, stp.getName().toString());
|
|
if (tehClass != null) {
|
|
// Method Expression is a simple name and wasn't located locally, but found in a class
|
|
// so look for method in this class.
|
|
return definedIn3rdPartyClass(ps, new ClassMember(tehClass), mi
|
|
.getName().toString());
|
|
}
|
|
//return new ClassMember(findClassIfExists(stp.getName().toString()));
|
|
}
|
|
//scopeParent = definedIn3rdPartyClass(stp.getName().toString(), "THIS");
|
|
return definedIn3rdPartyClass(ps, new ClassMember(ps, typeDec), mi
|
|
.getName().toString());
|
|
}
|
|
} else {
|
|
log("MI EXP.."+getNodeAsString(mi.getExpression()));
|
|
// return null;
|
|
scopeParent = resolveExpression3rdParty(ps, nearestNode,
|
|
mi.getExpression(), noCompare);
|
|
log("MI, ScopeParent " + scopeParent);
|
|
return definedIn3rdPartyClass(ps, scopeParent, mi.getName().toString());
|
|
}
|
|
|
|
}
|
|
break;
|
|
case ASTNode.QUALIFIED_NAME:
|
|
QualifiedName qn = (QualifiedName) astNode;
|
|
ASTNode temp2 = findDeclaration2(qn.getName(), nearestNode);
|
|
if(temp2 instanceof FieldDeclaration){
|
|
// field is locally defined
|
|
log(qn.getName() + " was found locally," + getNodeAsString(extracTypeInfo(temp2)));
|
|
return new ClassMember(ps, extracTypeInfo(temp2));
|
|
}
|
|
if (qn.getQualifier() == null) {
|
|
log("QN,Not implemented.");
|
|
return null;
|
|
} else {
|
|
|
|
if (qn.getQualifier() instanceof SimpleName) {
|
|
stp = extracTypeInfo(findDeclaration2(qn.getQualifier(), nearestNode));
|
|
if(stp == null){
|
|
/*The type wasn't found in local code, so it might be something like
|
|
* log(), or maybe belonging to super class, etc.
|
|
*/
|
|
Class<?> tehClass = findClassIfExists(ps, qn.getQualifier().toString());
|
|
if (tehClass != null) {
|
|
// note how similar thing is called on line 690. Check check.
|
|
return definedIn3rdPartyClass(ps, new ClassMember(tehClass), qn
|
|
.getName().toString());
|
|
}
|
|
log("QN resolve 3rd par, Can't resolve " + qn.getQualifier());
|
|
return null;
|
|
}
|
|
log("QN, SN Local Type " + getNodeAsString(stp));
|
|
//scopeParent = definedIn3rdPartyClass(stp.getName().toString(), "THIS");
|
|
ASTNode typeDec = findDeclaration2(stp.getName(),nearestNode);
|
|
if(typeDec == null){
|
|
log(stp.getName() + " couldn't be found locally..");
|
|
|
|
Class<?> tehClass = findClassIfExists(ps, stp.getName().toString());
|
|
if (tehClass != null) {
|
|
// note how similar thing is called on line 690. Check check.
|
|
return definedIn3rdPartyClass(ps, new ClassMember(tehClass), qn
|
|
.getName().toString());
|
|
}
|
|
log("QN resolve 3rd par, Can't resolve " + qn.getQualifier());
|
|
return null;
|
|
}
|
|
return definedIn3rdPartyClass(ps, new ClassMember(ps, typeDec), qn
|
|
.getName().toString());
|
|
} else {
|
|
scopeParent = resolveExpression3rdParty(ps, nearestNode,
|
|
qn.getQualifier(), noCompare);
|
|
log("QN, ScopeParent " + scopeParent);
|
|
return definedIn3rdPartyClass(ps, scopeParent, qn.getName().toString());
|
|
}
|
|
|
|
}
|
|
case ASTNode.ARRAY_ACCESS:
|
|
ArrayAccess arac = (ArrayAccess)astNode;
|
|
return resolveExpression3rdParty(ps, nearestNode, arac.getArray(), noCompare);
|
|
default:
|
|
log("Unaccounted type " + getNodeAsString(astNode));
|
|
break;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
public static Class<?> getArrayClass(String elementClass, ClassLoader classLoader) {
|
|
String name;
|
|
if (elementClass.startsWith("[")) {
|
|
// just add a leading "["
|
|
name = "[" + elementClass;
|
|
} else if (elementClass.equals("boolean")) {
|
|
name = "[Z";
|
|
} else if (elementClass.equals("byte")) {
|
|
name = "[B";
|
|
} else if (elementClass.equals("char")) {
|
|
name = "[C";
|
|
} else if (elementClass.equals("double")) {
|
|
name = "[D";
|
|
} else if (elementClass.equals("float")) {
|
|
name = "[F";
|
|
} else if (elementClass.equals("int")) {
|
|
name = "[I";
|
|
} else if (elementClass.equals("long")) {
|
|
name = "[J";
|
|
} else if (elementClass.equals("short")) {
|
|
name = "[S";
|
|
} else {
|
|
// must be an object non-array class
|
|
name = "[L" + elementClass + ";";
|
|
}
|
|
return loadClass(name, classLoader);
|
|
}
|
|
|
|
|
|
/**
|
|
* For a().abc.a123 this would return a123
|
|
*
|
|
* @param expression
|
|
* @return
|
|
*/
|
|
public static ASTNode getChildExpression(ASTNode expression) {
|
|
// ASTNode anode = null;
|
|
if (expression instanceof SimpleName) {
|
|
return expression;
|
|
} else if (expression instanceof FieldAccess) {
|
|
return ((FieldAccess) expression).getName();
|
|
} else if (expression instanceof QualifiedName) {
|
|
return ((QualifiedName) expression).getName();
|
|
}else if (expression instanceof MethodInvocation) {
|
|
return ((MethodInvocation) expression).getName();
|
|
}else if(expression instanceof ArrayAccess){
|
|
return ((ArrayAccess)expression).getArray();
|
|
}
|
|
log(" getChildExpression returning NULL for "
|
|
+ getNodeAsString(expression));
|
|
return null;
|
|
}
|
|
|
|
public static ASTNode getParentExpression(ASTNode expression) {
|
|
// ASTNode anode = null;
|
|
if (expression instanceof SimpleName) {
|
|
return expression;
|
|
} else if (expression instanceof FieldAccess) {
|
|
return ((FieldAccess) expression).getExpression();
|
|
} else if (expression instanceof QualifiedName) {
|
|
return ((QualifiedName) expression).getQualifier();
|
|
} else if (expression instanceof MethodInvocation) {
|
|
return ((MethodInvocation) expression).getExpression();
|
|
} else if (expression instanceof ArrayAccess) {
|
|
return ((ArrayAccess) expression).getArray();
|
|
}
|
|
log("getParentExpression returning NULL for "
|
|
+ getNodeAsString(expression));
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Loads classes from .jar files in sketch classpath
|
|
*
|
|
* @param typeName
|
|
* @param child
|
|
* @param noCompare
|
|
* @return
|
|
*/
|
|
public static ArrayList<CompletionCandidate> getMembersForType(PreprocSketch ps,
|
|
String typeName,
|
|
String child,
|
|
boolean noCompare,
|
|
boolean staticOnly) {
|
|
ArrayList<CompletionCandidate> candidates = new ArrayList<>();
|
|
log("In GMFT(), Looking for match " + child
|
|
+ " in class " + typeName + " noCompare " + noCompare + " staticOnly "
|
|
+ staticOnly);
|
|
Class<?> probableClass = findClassIfExists(ps, typeName);
|
|
if(probableClass == null){
|
|
log("In GMFT(), class not found.");
|
|
return candidates;
|
|
}
|
|
return getMembersForType(ps, new ClassMember(probableClass), child, noCompare, staticOnly);
|
|
|
|
}
|
|
|
|
public static ArrayList<CompletionCandidate> getMembersForType(PreprocSketch ps,
|
|
ClassMember tehClass,
|
|
String childToLookFor,
|
|
boolean noCompare,
|
|
boolean staticOnly) {
|
|
String child = childToLookFor.toLowerCase();
|
|
ArrayList<CompletionCandidate> candidates = new ArrayList<>();
|
|
log("getMemFoType-> Looking for match " + child
|
|
+ " inside " + tehClass + " noCompare " + noCompare + " staticOnly "
|
|
+ staticOnly);
|
|
if(tehClass == null){
|
|
return candidates;
|
|
}
|
|
// tehClass will either be a TypeDecl defined locally
|
|
if(tehClass.getDeclaringNode() instanceof TypeDeclaration){
|
|
TypeDeclaration td = (TypeDeclaration) tehClass.getDeclaringNode();
|
|
{
|
|
FieldDeclaration[] fields = td.getFields();
|
|
for (FieldDeclaration field : fields) {
|
|
if (staticOnly && !isStatic(field.modifiers())) {
|
|
continue;
|
|
}
|
|
List<VariableDeclarationFragment> vdfs = field.fragments();
|
|
for (VariableDeclarationFragment vdf : vdfs) {
|
|
if (noCompare) {
|
|
candidates.add(new CompletionCandidate(vdf));
|
|
} else if (vdf.getName().toString().toLowerCase().startsWith(child))
|
|
candidates.add(new CompletionCandidate(vdf));
|
|
}
|
|
}
|
|
}
|
|
{
|
|
MethodDeclaration[] methods = td.getMethods();
|
|
for (MethodDeclaration method : methods) {
|
|
if (staticOnly && !isStatic(method.modifiers())) {
|
|
continue;
|
|
}
|
|
if (noCompare) {
|
|
candidates.add(new CompletionCandidate(method));
|
|
} else if (method.getName().toString().toLowerCase()
|
|
.startsWith(child))
|
|
candidates.add(new CompletionCandidate(method));
|
|
}
|
|
}
|
|
|
|
ArrayList<CompletionCandidate> superClassCandidates;
|
|
if(td.getSuperclassType() != null){
|
|
log(getNodeAsString(td.getSuperclassType()) + " <-Looking into superclass of " + tehClass);
|
|
superClassCandidates = getMembersForType(ps, new ClassMember(ps, td
|
|
.getSuperclassType()),
|
|
childToLookFor, noCompare, staticOnly);
|
|
}
|
|
else
|
|
{
|
|
superClassCandidates = getMembersForType(ps, new ClassMember(Object.class),
|
|
childToLookFor, noCompare, staticOnly);
|
|
}
|
|
for (CompletionCandidate cc : superClassCandidates) {
|
|
candidates.add(cc);
|
|
}
|
|
return candidates;
|
|
}
|
|
|
|
// Or tehClass will be a predefined class
|
|
|
|
Class<?> probableClass;
|
|
if (tehClass.getClass_() != null) {
|
|
probableClass = tehClass.getClass_();
|
|
} else {
|
|
probableClass = findClassIfExists(ps, tehClass.getTypeAsString());
|
|
if (probableClass == null) {
|
|
log("Couldn't find class " + tehClass.getTypeAsString());
|
|
return candidates;
|
|
}
|
|
log("Loaded " + probableClass.toString());
|
|
}
|
|
for (Method method : probableClass.getMethods()) {
|
|
if (!Modifier.isStatic(method.getModifiers()) && staticOnly) {
|
|
continue;
|
|
}
|
|
|
|
StringBuilder label = new StringBuilder(method.getName() + "(");
|
|
for (int i = 0; i < method.getParameterTypes().length; i++) {
|
|
label.append(method.getParameterTypes()[i].getSimpleName());
|
|
if (i < method.getParameterTypes().length - 1)
|
|
label.append(",");
|
|
}
|
|
label.append(")");
|
|
if (noCompare) {
|
|
candidates.add(new CompletionCandidate(method));
|
|
} else if (label.toString().toLowerCase().startsWith(child)) {
|
|
candidates.add(new CompletionCandidate(method));
|
|
}
|
|
}
|
|
for (Field field : probableClass.getFields()) {
|
|
if (!Modifier.isStatic(field.getModifiers()) && staticOnly) {
|
|
continue;
|
|
}
|
|
if (noCompare) {
|
|
candidates.add(new CompletionCandidate(field));
|
|
} else if (field.getName().toLowerCase().startsWith(child)) {
|
|
candidates.add(new CompletionCandidate(field));
|
|
}
|
|
}
|
|
if (probableClass.isArray() && !staticOnly) {
|
|
// add array members manually, they can't be fetched through code
|
|
|
|
String className = probableClass.getSimpleName();
|
|
|
|
if (noCompare || "clone()".startsWith(child)) {
|
|
String methodLabel = "<html>clone() : " + className +
|
|
" - <font color=#777777>" + className + "</font></html>";
|
|
candidates.add(new CompletionCandidate("clone()", methodLabel, "clone()",
|
|
CompletionCandidate.PREDEF_METHOD));
|
|
}
|
|
|
|
if ("length".startsWith(child)) {
|
|
String fieldLabel = "<html>length : int - <font color=#777777>" +
|
|
className + "</font></html>";
|
|
candidates.add(new CompletionCandidate("length", fieldLabel, "length",
|
|
CompletionCandidate.PREDEF_FIELD));
|
|
}
|
|
}
|
|
return candidates;
|
|
}
|
|
|
|
private static boolean isStatic(List<org.eclipse.jdt.core.dom.Modifier> modifiers) {
|
|
for (org.eclipse.jdt.core.dom.Modifier m : modifiers) {
|
|
if (m.isStatic()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Searches for the particular class in the default list of imports as well as
|
|
* the Sketch classpath
|
|
* @param className
|
|
* @return
|
|
*/
|
|
protected static Class<?> findClassIfExists(PreprocSketch ps, String className){
|
|
if (className == null){
|
|
return null;
|
|
}
|
|
|
|
if (className.indexOf('.') >= 0) {
|
|
// Figure out what is package and what is class
|
|
String[] parts = className.split("\\.");
|
|
String newClassName = parts[0];
|
|
int i = 1;
|
|
while (i < parts.length &&
|
|
ps.classPath.isPackage(newClassName)) {
|
|
newClassName = newClassName + "/" + parts[i++];
|
|
}
|
|
while (i < parts.length) {
|
|
newClassName = newClassName + "$" + parts[i++];
|
|
}
|
|
className = newClassName.replace('/', '.');
|
|
}
|
|
|
|
// First, see if the classname is a fully qualified name and loads straightaway
|
|
Class<?> tehClass = loadClass(className, ps.classLoader);
|
|
|
|
if (tehClass != null) {
|
|
//log(tehClass.getName() + " located straightaway");
|
|
return tehClass;
|
|
}
|
|
|
|
// This name is qualified and it already had its chance
|
|
if (className.indexOf('.') >= 0) {
|
|
return null;
|
|
}
|
|
|
|
log("Looking in the classloader for " + className);
|
|
// Using ClassPath and RegExResourceFilter to find a matching class
|
|
// and then loading the thing might be simpler and faster
|
|
|
|
// These can be preprocessed during error check for performance
|
|
// (collect, split into starred and not starred)
|
|
List<ImportStatement> programImports = ps.programImports;
|
|
List<ImportStatement> codeFolderImports = ps.codeFolderImports;
|
|
List<ImportStatement> coreAndDefaultImports = ps.coreAndDefaultImports;
|
|
|
|
ImportStatement javaLang = ImportStatement.wholePackage("java.lang");
|
|
|
|
Stream<List<ImportStatement>> importListStream =
|
|
Stream.of(Collections.singletonList(javaLang), coreAndDefaultImports,
|
|
programImports, codeFolderImports);
|
|
|
|
final String finalClassName = className;
|
|
|
|
// These streams can be made unordered parallel if it helps performance
|
|
return importListStream
|
|
.map(list -> list.stream()
|
|
.map(is -> {
|
|
if (is.getMemberName().equals(finalClassName)) {
|
|
return is.getFullMemberName();
|
|
} else if (is.isStarredImport()) {
|
|
return is.getPackageName() + "." + finalClassName;
|
|
}
|
|
return null;
|
|
})
|
|
.filter(name -> name != null)
|
|
.map(name -> loadClass(name, ps.classLoader))
|
|
.filter(cls -> cls != null)
|
|
.findAny())
|
|
.filter(Optional::isPresent)
|
|
.map(Optional::get)
|
|
.findAny()
|
|
.orElse(null);
|
|
}
|
|
|
|
protected static Class<?> loadClass(String className, ClassLoader classLoader){
|
|
Class<?> tehClass = null;
|
|
if (className != null) {
|
|
try {
|
|
tehClass = Class.forName(className, false, classLoader);
|
|
} catch (ClassNotFoundException e) {
|
|
//log("Doesn't exist in package: ");
|
|
}
|
|
}
|
|
return tehClass;
|
|
}
|
|
|
|
public static ClassMember definedIn3rdPartyClass(PreprocSketch ps, String className,String memberName){
|
|
Class<?> probableClass = findClassIfExists(ps, className);
|
|
if (probableClass == null) {
|
|
log("Couldn't load " + className);
|
|
return null;
|
|
}
|
|
if (memberName.equals("THIS")) {
|
|
return new ClassMember(probableClass);
|
|
} else {
|
|
return definedIn3rdPartyClass(ps, new ClassMember(probableClass), memberName);
|
|
}
|
|
}
|
|
|
|
public static ClassMember definedIn3rdPartyClass(PreprocSketch ps, ClassMember tehClass,String memberName){
|
|
if(tehClass == null)
|
|
return null;
|
|
log("definedIn3rdPartyClass-> Looking for " + memberName
|
|
+ " in " + tehClass);
|
|
String memberNameL = memberName.toLowerCase();
|
|
if (tehClass.getDeclaringNode() instanceof TypeDeclaration) {
|
|
|
|
TypeDeclaration td = (TypeDeclaration) tehClass.getDeclaringNode();
|
|
for (int i = 0; i < td.getFields().length; i++) {
|
|
List<VariableDeclarationFragment> vdfs =
|
|
td.getFields()[i].fragments();
|
|
for (VariableDeclarationFragment vdf : vdfs) {
|
|
if (vdf.getName().toString().toLowerCase()
|
|
.startsWith(memberNameL))
|
|
return new ClassMember(ps, vdf);
|
|
}
|
|
|
|
}
|
|
for (int i = 0; i < td.getMethods().length; i++) {
|
|
if (td.getMethods()[i].getName().toString().toLowerCase()
|
|
.startsWith(memberNameL))
|
|
return new ClassMember(ps, td.getMethods()[i]);
|
|
}
|
|
if (td.getSuperclassType() != null) {
|
|
log(getNodeAsString(td.getSuperclassType()) + " <-Looking into superclass of " + tehClass);
|
|
return definedIn3rdPartyClass(ps, new ClassMember(ps, td
|
|
.getSuperclassType()),memberName);
|
|
} else {
|
|
return definedIn3rdPartyClass(ps, new ClassMember(Object.class),memberName);
|
|
}
|
|
}
|
|
|
|
Class<?> probableClass;
|
|
if (tehClass.getClass_() != null) {
|
|
probableClass = tehClass.getClass_();
|
|
} else {
|
|
probableClass = findClassIfExists(ps, tehClass.getTypeAsString());
|
|
log("Loaded " + probableClass.toString());
|
|
}
|
|
for (Method method : probableClass.getMethods()) {
|
|
if (method.getName().equalsIgnoreCase(memberName)) {
|
|
return new ClassMember(method);
|
|
}
|
|
}
|
|
for (Field field : probableClass.getFields()) {
|
|
if (field.getName().equalsIgnoreCase(memberName)) {
|
|
return new ClassMember(field);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
protected static ASTNode findClosestParentNode(int lineNumber, ASTNode node) {
|
|
// Base.loge("Props of " + node.getClass().getName());
|
|
for (StructuralPropertyDescriptor prop : (Iterable<StructuralPropertyDescriptor>) node
|
|
.structuralPropertiesForType()) {
|
|
if (prop.isChildProperty() || prop.isSimpleProperty()) {
|
|
if (node.getStructuralProperty(prop) != null) {
|
|
// System.out
|
|
// .println(node.getStructuralProperty(prop) + " -> " + (prop));
|
|
if (node.getStructuralProperty(prop) instanceof ASTNode) {
|
|
ASTNode cnode = (ASTNode) node.getStructuralProperty(prop);
|
|
// log("Looking at " + getNodeAsString(cnode)+ " for line num " + lineNumber);
|
|
int cLineNum = ((CompilationUnit) cnode.getRoot())
|
|
.getLineNumber(cnode.getStartPosition() + cnode.getLength());
|
|
if (getLineNumber(cnode) <= lineNumber && lineNumber <= cLineNum) {
|
|
return findClosestParentNode(lineNumber, cnode);
|
|
}
|
|
}
|
|
}
|
|
} else if (prop.isChildListProperty()) {
|
|
List<ASTNode> nodelist = (List<ASTNode>) node
|
|
.getStructuralProperty(prop);
|
|
for (ASTNode cnode : nodelist) {
|
|
int cLineNum = ((CompilationUnit) cnode.getRoot())
|
|
.getLineNumber(cnode.getStartPosition() + cnode.getLength());
|
|
// log("Looking at " + getNodeAsString(cnode)+ " for line num " + lineNumber);
|
|
if (getLineNumber(cnode) <= lineNumber && lineNumber <= cLineNum) {
|
|
return findClosestParentNode(lineNumber, cnode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
protected static ASTNode findClosestNode(int lineNumber, ASTNode node) {
|
|
log("findClosestNode to line " + lineNumber);
|
|
ASTNode parent = findClosestParentNode(lineNumber, node);
|
|
log("findClosestParentNode returned " + getNodeAsString(parent));
|
|
if (parent == null)
|
|
return null;
|
|
if (getLineNumber(parent) == lineNumber){
|
|
log(parent + "|PNode " + getLineNumber(parent) + ", lfor " + lineNumber );
|
|
return parent;
|
|
}
|
|
List<ASTNode> nodes;
|
|
if (parent instanceof TypeDeclaration) {
|
|
nodes = ((TypeDeclaration) parent).bodyDeclarations();
|
|
} else if (parent instanceof Block) {
|
|
nodes = ((Block) parent).statements();
|
|
} else {
|
|
log("findClosestNode() found " + getNodeAsString(parent));
|
|
return null;
|
|
}
|
|
|
|
if (nodes.size() > 0) {
|
|
ASTNode retNode = parent;
|
|
for (ASTNode cNode : nodes) {
|
|
log(cNode + "|cNode " + getLineNumber(cNode) + ", lfor " + lineNumber);
|
|
if (getLineNumber(cNode) <= lineNumber)
|
|
retNode = cNode;
|
|
}
|
|
|
|
return retNode;
|
|
}
|
|
return parent;
|
|
}
|
|
|
|
|
|
/**
|
|
* Fetches line number of the node in its CompilationUnit.
|
|
* @param node
|
|
* @return
|
|
*/
|
|
public static int getLineNumber(ASTNode node) {
|
|
return ((CompilationUnit) node.getRoot()).getLineNumber(node
|
|
.getStartPosition());
|
|
}
|
|
|
|
|
|
/*
|
|
protected SketchOutline sketchOutline;
|
|
|
|
public void showSketchOutline() {
|
|
if (editor.hasJavaTabs()) return;
|
|
|
|
sketchOutline = new SketchOutline(editor, codeTree);
|
|
sketchOutline.show();
|
|
}
|
|
|
|
|
|
public void showTabOutline() {
|
|
new TabOutline(editor).show();
|
|
}
|
|
*/
|
|
|
|
|
|
/**
|
|
* Give this thing a {@link Name} instance - a {@link SimpleName} from the
|
|
* ASTNode for ex, and it tries its level best to locate its declaration in
|
|
* the AST. It really does.
|
|
*
|
|
* @param findMe
|
|
* @return
|
|
*/
|
|
protected static ASTNode findDeclaration(Name findMe) {
|
|
|
|
// WARNING: You're entering the Rube Goldberg territory of Experimental Mode.
|
|
// To debug this code, thou must take the Recursive Leap of Faith.
|
|
|
|
// log("entering --findDeclaration1 -- " + findMe.toString());
|
|
ASTNode declaringClass;
|
|
ASTNode parent = findMe.getParent();
|
|
ASTNode ret;
|
|
ArrayList<Integer> constrains = new ArrayList<>();
|
|
if (parent.getNodeType() == ASTNode.METHOD_INVOCATION) {
|
|
Expression exp = (Expression) parent.getStructuralProperty(MethodInvocation.EXPRESSION_PROPERTY);
|
|
//TODO: Note the imbalance of constrains.add(ASTNode.METHOD_DECLARATION);
|
|
// Possibly a bug here. Investigate later.
|
|
if (((MethodInvocation) parent).getName().toString()
|
|
.equals(findMe.toString())) {
|
|
constrains.add(ASTNode.METHOD_DECLARATION);
|
|
|
|
if (exp != null) {
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
// log("MI EXP: " + exp.toString() + " of type "
|
|
// + exp.getClass().getName() + " parent: " + exp.getParent());
|
|
if (exp instanceof MethodInvocation) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration(((MethodInvocation) exp)
|
|
.getName()));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration(stp.getName());
|
|
return definedIn(declaringClass, ((MethodInvocation) parent)
|
|
.getName().toString(), constrains);
|
|
} else if (exp instanceof FieldAccess) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration(((FieldAccess) exp)
|
|
.getName()));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration((stp.getName()));
|
|
return definedIn(declaringClass, ((MethodInvocation) parent)
|
|
.getName().toString(), constrains);
|
|
}
|
|
if (exp instanceof SimpleName) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration(((SimpleName) exp)));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration(stp.getName());
|
|
// log("MI.SN " + getNodeAsString(declaringClass));
|
|
constrains.add(ASTNode.METHOD_DECLARATION);
|
|
return definedIn(declaringClass, ((MethodInvocation) parent)
|
|
.getName().toString(), constrains);
|
|
}
|
|
|
|
}
|
|
} else {
|
|
parent = parent.getParent(); // Move one up the ast. V V IMP!!
|
|
}
|
|
} else if (parent.getNodeType() == ASTNode.FIELD_ACCESS) {
|
|
FieldAccess fa = (FieldAccess) parent;
|
|
Expression exp = fa.getExpression();
|
|
if (fa.getName().toString().equals(findMe.toString())) {
|
|
constrains.add(ASTNode.FIELD_DECLARATION);
|
|
|
|
if (exp != null) {
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
// log("FA EXP: " + exp.toString() + " of type "
|
|
// + exp.getClass().getName() + " parent: " + exp.getParent());
|
|
if (exp instanceof MethodInvocation) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration(((MethodInvocation) exp)
|
|
.getName()));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration(stp.getName());
|
|
return definedIn(declaringClass, fa.getName().toString(),
|
|
constrains);
|
|
} else if (exp instanceof FieldAccess) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration(((FieldAccess) exp)
|
|
.getName()));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration((stp.getName()));
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
return definedIn(declaringClass, fa.getName().toString(),
|
|
constrains);
|
|
}
|
|
if (exp instanceof SimpleName) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration(((SimpleName) exp)));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration(stp.getName());
|
|
// log("FA.SN " + getNodeAsString(declaringClass));
|
|
constrains.add(ASTNode.METHOD_DECLARATION);
|
|
return definedIn(declaringClass, fa.getName().toString(),
|
|
constrains);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
parent = parent.getParent(); // Move one up the ast. V V IMP!!
|
|
}
|
|
} else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME) {
|
|
|
|
QualifiedName qn = (QualifiedName) parent;
|
|
if (!findMe.toString().equals(qn.getQualifier().toString())) {
|
|
|
|
SimpleType stp = extracTypeInfo(findDeclaration((qn.getQualifier())));
|
|
// log(qn.getQualifier() + "->" + qn.getName());
|
|
if (stp == null) {
|
|
return null;
|
|
}
|
|
|
|
declaringClass = findDeclaration(stp.getName());
|
|
|
|
// log("QN decl class: " + getNodeAsString(declaringClass));
|
|
constrains.clear();
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
constrains.add(ASTNode.FIELD_DECLARATION);
|
|
return definedIn(declaringClass, qn.getName().toString(), constrains);
|
|
}
|
|
else{
|
|
if(findMe instanceof QualifiedName){
|
|
QualifiedName qnn = (QualifiedName) findMe;
|
|
// log("findMe is a QN, "
|
|
// + (qnn.getQualifier().toString() + " other " + qnn.getName()
|
|
// .toString()));
|
|
|
|
SimpleType stp = extracTypeInfo(findDeclaration((qnn.getQualifier())));
|
|
if (stp == null) {
|
|
return null;
|
|
}
|
|
declaringClass = findDeclaration(stp.getName());
|
|
constrains.clear();
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
constrains.add(ASTNode.FIELD_DECLARATION);
|
|
return definedIn(declaringClass, qnn.getName().toString(),
|
|
constrains);
|
|
}
|
|
}
|
|
} else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE) {
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
if (parent.getParent().getNodeType() == ASTNode.CLASS_INSTANCE_CREATION) {
|
|
constrains.add(ASTNode.CLASS_INSTANCE_CREATION);
|
|
}
|
|
} else if (parent.getNodeType() == ASTNode.TYPE_DECLARATION) {
|
|
// The condition where we look up the name of a class decl
|
|
TypeDeclaration td = (TypeDeclaration) parent;
|
|
if (findMe.equals(td.getName())) {
|
|
return parent;
|
|
}
|
|
|
|
} else if (parent instanceof Expression) {
|
|
// constrains.add(ASTNode.TYPE_DECLARATION);
|
|
// constrains.add(ASTNode.METHOD_DECLARATION);
|
|
// constrains.add(ASTNode.FIELD_DECLARATION);
|
|
}
|
|
// else if(findMe instanceof QualifiedName){
|
|
// QualifiedName qn = (QualifiedName) findMe;
|
|
// System.out
|
|
// .println("findMe is a QN, "
|
|
// + (qn.getQualifier().toString() + " other " + qn.getName()
|
|
// .toString()));
|
|
// }
|
|
while (parent != null) {
|
|
// log("findDeclaration1 -> " + getNodeAsString(parent));
|
|
for (Object oprop : parent.structuralPropertiesForType()) {
|
|
StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) oprop;
|
|
if (prop.isChildProperty() || prop.isSimpleProperty()) {
|
|
if (parent.getStructuralProperty(prop) instanceof ASTNode) {
|
|
// log(prop + " C/S Prop of -> "
|
|
// + getNodeAsString(parent));
|
|
ret = definedIn((ASTNode) parent.getStructuralProperty(prop),
|
|
findMe.toString(), constrains);
|
|
if (ret != null)
|
|
return ret;
|
|
}
|
|
} else if (prop.isChildListProperty()) {
|
|
// log((prop) + " ChildList props of "
|
|
// + getNodeAsString(parent));
|
|
List<ASTNode> nodelist = (List<ASTNode>) parent
|
|
.getStructuralProperty(prop);
|
|
for (ASTNode retNode : nodelist) {
|
|
ret = definedIn(retNode, findMe.toString(), constrains);
|
|
if (ret != null)
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
parent = parent.getParent();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* A variation of findDeclaration() but accepts an alternate parent ASTNode
|
|
* @param findMe
|
|
* @param alternateParent
|
|
* @return
|
|
*/
|
|
protected static ASTNode findDeclaration2(Name findMe, ASTNode alternateParent) {
|
|
ASTNode declaringClass;
|
|
ASTNode parent = findMe.getParent();
|
|
ASTNode ret;
|
|
ArrayList<Integer> constrains = new ArrayList<>();
|
|
if (parent.getNodeType() == ASTNode.METHOD_INVOCATION) {
|
|
Expression exp = (Expression) parent.getStructuralProperty(MethodInvocation.EXPRESSION_PROPERTY);
|
|
//TODO: Note the imbalance of constrains.add(ASTNode.METHOD_DECLARATION);
|
|
// Possibly a bug here. Investigate later.
|
|
if (((MethodInvocation) parent).getName().toString()
|
|
.equals(findMe.toString())) {
|
|
constrains.add(ASTNode.METHOD_DECLARATION);
|
|
|
|
if (exp != null) {
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
// log("MI EXP: " + exp.toString() + " of type "
|
|
// + exp.getClass().getName() + " parent: " + exp.getParent());
|
|
if (exp instanceof MethodInvocation) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration2(((MethodInvocation) exp)
|
|
.getName(),
|
|
alternateParent));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration2(stp.getName(), alternateParent);
|
|
return definedIn(declaringClass, ((MethodInvocation) parent)
|
|
.getName().toString(), constrains);
|
|
} else if (exp instanceof FieldAccess) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration2(((FieldAccess) exp)
|
|
.getName(),
|
|
alternateParent));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration2((stp.getName()), alternateParent);
|
|
return definedIn(declaringClass, ((MethodInvocation) parent)
|
|
.getName().toString(), constrains);
|
|
}
|
|
if (exp instanceof SimpleName) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration2(((SimpleName) exp),
|
|
alternateParent));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration2(stp.getName(), alternateParent);
|
|
// log("MI.SN " + getNodeAsString(declaringClass));
|
|
constrains.add(ASTNode.METHOD_DECLARATION);
|
|
return definedIn(declaringClass, ((MethodInvocation) parent)
|
|
.getName().toString(), constrains);
|
|
}
|
|
|
|
}
|
|
} else {
|
|
parent = parent.getParent(); // Move one up the ast. V V IMP!!
|
|
alternateParent = alternateParent.getParent();
|
|
}
|
|
} else if (parent.getNodeType() == ASTNode.FIELD_ACCESS) {
|
|
FieldAccess fa = (FieldAccess) parent;
|
|
Expression exp = fa.getExpression();
|
|
if (fa.getName().toString().equals(findMe.toString())) {
|
|
constrains.add(ASTNode.FIELD_DECLARATION);
|
|
|
|
if (exp != null) {
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
// log("FA EXP: " + exp.toString() + " of type "
|
|
// + exp.getClass().getName() + " parent: " + exp.getParent());
|
|
if (exp instanceof MethodInvocation) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration2(((MethodInvocation) exp)
|
|
.getName(),
|
|
alternateParent));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration2(stp.getName(), alternateParent);
|
|
return definedIn(declaringClass, fa.getName().toString(),
|
|
constrains);
|
|
} else if (exp instanceof FieldAccess) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration2(((FieldAccess) exp)
|
|
.getName(),
|
|
alternateParent));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration2((stp.getName()), alternateParent);
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
return definedIn(declaringClass, fa.getName().toString(),
|
|
constrains);
|
|
}
|
|
if (exp instanceof SimpleName) {
|
|
SimpleType stp = extracTypeInfo(findDeclaration2(((SimpleName) exp),
|
|
alternateParent));
|
|
if (stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration2(stp.getName(), alternateParent);
|
|
// log("FA.SN " + getNodeAsString(declaringClass));
|
|
constrains.add(ASTNode.METHOD_DECLARATION);
|
|
return definedIn(declaringClass, fa.getName().toString(),
|
|
constrains);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
parent = parent.getParent(); // Move one up the ast. V V IMP!!
|
|
alternateParent = alternateParent.getParent();
|
|
}
|
|
} else if (parent.getNodeType() == ASTNode.QUALIFIED_NAME) {
|
|
|
|
QualifiedName qn = (QualifiedName) parent;
|
|
if (!findMe.toString().equals(qn.getQualifier().toString())) {
|
|
|
|
SimpleType stp = extracTypeInfo(findDeclaration2((qn.getQualifier()),
|
|
alternateParent));
|
|
if(stp == null)
|
|
return null;
|
|
declaringClass = findDeclaration2(stp.getName(), alternateParent);
|
|
// log(qn.getQualifier() + "->" + qn.getName());
|
|
// log("QN decl class: " + getNodeAsString(declaringClass));
|
|
constrains.clear();
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
constrains.add(ASTNode.FIELD_DECLARATION);
|
|
return definedIn(declaringClass, qn.getName().toString(), constrains);
|
|
}
|
|
else{
|
|
if(findMe instanceof QualifiedName){
|
|
QualifiedName qnn = (QualifiedName) findMe;
|
|
// log("findMe is a QN, "
|
|
// + (qnn.getQualifier().toString() + " other " + qnn.getName()
|
|
// .toString()));
|
|
|
|
SimpleType stp = extracTypeInfo(findDeclaration2((qnn.getQualifier()), alternateParent));
|
|
|
|
if (stp == null) {
|
|
return null;
|
|
}
|
|
|
|
// log(qnn.getQualifier() + "->" + qnn.getName());
|
|
declaringClass = findDeclaration2(stp.getName(), alternateParent);
|
|
|
|
// log("QN decl class: "
|
|
// + getNodeAsString(declaringClass));
|
|
constrains.clear();
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
constrains.add(ASTNode.FIELD_DECLARATION);
|
|
return definedIn(declaringClass, qnn.getName().toString(), constrains);
|
|
}
|
|
}
|
|
} else if (parent.getNodeType() == ASTNode.SIMPLE_TYPE) {
|
|
constrains.add(ASTNode.TYPE_DECLARATION);
|
|
if (parent.getParent().getNodeType() == ASTNode.CLASS_INSTANCE_CREATION)
|
|
constrains.add(ASTNode.CLASS_INSTANCE_CREATION);
|
|
} else if (parent instanceof Expression) {
|
|
// constrains.add(ASTNode.TYPE_DECLARATION);
|
|
// constrains.add(ASTNode.METHOD_DECLARATION);
|
|
// constrains.add(ASTNode.FIELD_DECLARATION);
|
|
} // TODO: in findDec, we also have a case where parent of type TD is handled.
|
|
// Figure out if needed here as well.
|
|
// log("Alternate parent: " + getNodeAsString(alternateParent));
|
|
while (alternateParent != null) {
|
|
// log("findDeclaration2 -> "
|
|
// + getNodeAsString(alternateParent));
|
|
for (Object oprop : alternateParent.structuralPropertiesForType()) {
|
|
StructuralPropertyDescriptor prop = (StructuralPropertyDescriptor) oprop;
|
|
if (prop.isChildProperty() || prop.isSimpleProperty()) {
|
|
if (alternateParent.getStructuralProperty(prop) instanceof ASTNode) {
|
|
// log(prop + " C/S Prop of -> "
|
|
// + getNodeAsString(alternateParent));
|
|
ret = definedIn((ASTNode) alternateParent
|
|
.getStructuralProperty(prop),
|
|
findMe.toString(), constrains);
|
|
if (ret != null)
|
|
return ret;
|
|
}
|
|
} else if (prop.isChildListProperty()) {
|
|
// log((prop) + " ChildList props of "
|
|
// + getNodeAsString(alternateParent));
|
|
List<ASTNode> nodelist = (List<ASTNode>) alternateParent
|
|
.getStructuralProperty(prop);
|
|
for (ASTNode retNode : nodelist) {
|
|
ret = definedIn(retNode, findMe.toString(), constrains);
|
|
if (ret != null)
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
alternateParent = alternateParent.getParent();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
protected static boolean ignorableSuggestionImport(PreprocSketch ps, String impName) {
|
|
|
|
String impNameLc = impName.toLowerCase();
|
|
|
|
List<ImportStatement> programImports = ps.programImports;
|
|
List<ImportStatement> codeFolderImports = ps.codeFolderImports;
|
|
|
|
boolean isImported = Stream
|
|
.concat(programImports.stream(), codeFolderImports.stream())
|
|
.anyMatch(impS -> {
|
|
String packageNameLc = impS.getPackageName().toLowerCase();
|
|
return impNameLc.startsWith(packageNameLc);
|
|
});
|
|
|
|
if (isImported) return false;
|
|
|
|
final String include = "include";
|
|
final String exclude = "exclude";
|
|
|
|
if (impName.startsWith("processing")) {
|
|
if (JavaMode.suggestionsMap.containsKey(include) && JavaMode.suggestionsMap.get(include).contains(impName)) {
|
|
return false;
|
|
} else if (JavaMode.suggestionsMap.containsKey(exclude) && JavaMode.suggestionsMap.get(exclude).contains(impName)) {
|
|
return true;
|
|
}
|
|
} else if (impName.startsWith("java")) {
|
|
if (JavaMode.suggestionsMap.containsKey(include) && JavaMode.suggestionsMap.get(include).contains(impName)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* A wrapper for java.lang.reflect types.
|
|
* Will have to see if the usage turns out to be internal only here or not
|
|
* and then accordingly decide where to place this class.
|
|
* @author quarkninja
|
|
*
|
|
*/
|
|
public static class ClassMember {
|
|
private Field field;
|
|
|
|
private Method method;
|
|
|
|
private Constructor<?> cons;
|
|
|
|
private Class<?> thisclass;
|
|
|
|
private String stringVal;
|
|
|
|
private String classType;
|
|
|
|
private ASTNode astNode;
|
|
|
|
private ASTNode declaringNode;
|
|
|
|
public ClassMember(Class<?> m) {
|
|
thisclass = m;
|
|
stringVal = "Predefined Class " + m.getName();
|
|
classType = m.getName();
|
|
}
|
|
|
|
public ClassMember(Method m) {
|
|
method = m;
|
|
stringVal = "Method " + m.getReturnType().getName() + " | " + m.getName()
|
|
+ " defined in " + m.getDeclaringClass().getName();
|
|
classType = m.getReturnType().getName();
|
|
}
|
|
|
|
public ClassMember(Field m) {
|
|
field = m;
|
|
stringVal = "Field " + m.getType().getName() + " | " + m.getName()
|
|
+ " defined in " + m.getDeclaringClass().getName();
|
|
classType = m.getType().getName();
|
|
}
|
|
|
|
public ClassMember(Constructor<?> m) {
|
|
cons = m;
|
|
stringVal = "Cons " + " " + m.getName() + " defined in "
|
|
+ m.getDeclaringClass().getName();
|
|
}
|
|
|
|
public ClassMember(PreprocSketch ps, ASTNode node){
|
|
astNode = node;
|
|
stringVal = getNodeAsString(node);
|
|
if(node instanceof TypeDeclaration){
|
|
declaringNode = node;
|
|
}
|
|
if(node instanceof SimpleType){
|
|
classType = ((SimpleType)node).getName().toString();
|
|
}
|
|
SimpleType stp = (node instanceof SimpleType) ? (SimpleType) node
|
|
: extracTypeInfo(node);
|
|
if(stp != null){
|
|
ASTNode decl =findDeclaration(stp.getName());
|
|
// Czech out teh mutation
|
|
if(decl == null){
|
|
// a predefined type
|
|
classType = stp.getName().toString();
|
|
thisclass = findClassIfExists(ps, classType);
|
|
}
|
|
else{
|
|
// a local type
|
|
declaringNode = decl;
|
|
}
|
|
}
|
|
}
|
|
|
|
public Class<?> getClass_() {
|
|
return thisclass;
|
|
}
|
|
|
|
public ASTNode getDeclaringNode(){
|
|
return declaringNode;
|
|
}
|
|
|
|
public Field getField() {
|
|
return field;
|
|
}
|
|
|
|
public Method getMethod() {
|
|
return method;
|
|
}
|
|
|
|
public Constructor<?> getCons() {
|
|
return cons;
|
|
}
|
|
|
|
public ASTNode getASTNode(){
|
|
return astNode;
|
|
}
|
|
|
|
public String toString() {
|
|
return stringVal;
|
|
}
|
|
|
|
public String getTypeAsString(){
|
|
return classType;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Find the SimpleType from FD, SVD, VDS, etc
|
|
*
|
|
* @param node
|
|
* @return
|
|
*/
|
|
public static SimpleType extracTypeInfo(ASTNode node) {
|
|
if (node == null) {
|
|
return null;
|
|
}
|
|
Type t = extracTypeInfo2(node);
|
|
if (t instanceof PrimitiveType) {
|
|
return null;
|
|
} else if (t instanceof ArrayType) {
|
|
ArrayType at = (ArrayType) t;
|
|
log("ele type "
|
|
+ at.getElementType() + ", "
|
|
+ at.getElementType().getClass().getName());
|
|
if (at.getElementType() instanceof PrimitiveType) {
|
|
return null;
|
|
} else if (at.getElementType() instanceof SimpleType) {
|
|
return (SimpleType) at.getElementType();
|
|
} else
|
|
return null;
|
|
} else if (t instanceof ParameterizedType) {
|
|
ParameterizedType pmt = (ParameterizedType) t;
|
|
log(pmt.getType() + ", " + pmt.getType().getClass());
|
|
if (pmt.getType() instanceof SimpleType) {
|
|
return (SimpleType) pmt.getType();
|
|
} else
|
|
return null;
|
|
}
|
|
return (SimpleType) t;
|
|
}
|
|
|
|
|
|
static public Type extracTypeInfo2(ASTNode node) {
|
|
Messages.log("* extracTypeInfo2");
|
|
if (node == null)
|
|
return null;
|
|
switch (node.getNodeType()) {
|
|
case ASTNode.METHOD_DECLARATION:
|
|
return ((MethodDeclaration) node).getReturnType2();
|
|
case ASTNode.FIELD_DECLARATION:
|
|
return ((FieldDeclaration) node).getType();
|
|
case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
|
|
return ((VariableDeclarationExpression) node).getType();
|
|
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
|
|
return ((VariableDeclarationStatement) node).getType();
|
|
case ASTNode.SINGLE_VARIABLE_DECLARATION:
|
|
return ((SingleVariableDeclaration) node).getType();
|
|
case ASTNode.VARIABLE_DECLARATION_FRAGMENT:
|
|
return extracTypeInfo2(node.getParent());
|
|
}
|
|
log("Unknown type info request " + getNodeAsString(node));
|
|
return null;
|
|
}
|
|
|
|
|
|
static protected ASTNode definedIn(ASTNode node, String name,
|
|
ArrayList<Integer> constrains) {
|
|
if (node == null)
|
|
return null;
|
|
if (constrains != null) {
|
|
// log("Looking at " + getNodeAsString(node) + " for " + name
|
|
// + " in definedIn");
|
|
if (!constrains.contains(node.getNodeType()) && constrains.size() > 0) {
|
|
// System.err.print("definedIn -1 " + " But constrain was ");
|
|
// for (Integer integer : constrains) {
|
|
// System.out.print(ASTNode.nodeClassForType(integer) + ",");
|
|
// }
|
|
// log();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
List<VariableDeclarationFragment> vdfList = null;
|
|
switch (node.getNodeType()) {
|
|
|
|
case ASTNode.TYPE_DECLARATION:
|
|
//Base.loge(getNodeAsString(node));
|
|
TypeDeclaration td = (TypeDeclaration) node;
|
|
if (td.getName().toString().equals(name)) {
|
|
if (constrains.contains(ASTNode.CLASS_INSTANCE_CREATION)) {
|
|
// look for constructor;
|
|
MethodDeclaration[] methods = td.getMethods();
|
|
for (MethodDeclaration md : methods) {
|
|
if (md.getName().toString().equalsIgnoreCase(name)) {
|
|
log("Found a constructor.");
|
|
return md;
|
|
}
|
|
}
|
|
} else {
|
|
// it's just the TD we're lookin for
|
|
return node;
|
|
}
|
|
} else {
|
|
if (constrains.contains(ASTNode.FIELD_DECLARATION)) {
|
|
// look for fields
|
|
FieldDeclaration[] fields = td.getFields();
|
|
for (FieldDeclaration fd : fields) {
|
|
List<VariableDeclarationFragment> fragments = fd.fragments();
|
|
for (VariableDeclarationFragment vdf : fragments) {
|
|
if (vdf.getName().toString().equalsIgnoreCase(name))
|
|
return fd;
|
|
}
|
|
}
|
|
} else if (constrains.contains(ASTNode.METHOD_DECLARATION)) {
|
|
// look for methods
|
|
MethodDeclaration[] methods = td.getMethods();
|
|
for (MethodDeclaration md : methods) {
|
|
if (md.getName().toString().equalsIgnoreCase(name)) {
|
|
return md;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case ASTNode.METHOD_DECLARATION:
|
|
//Base.loge(getNodeAsString(node));
|
|
if (((MethodDeclaration) node).getName().toString().equalsIgnoreCase(name))
|
|
return node;
|
|
break;
|
|
case ASTNode.SINGLE_VARIABLE_DECLARATION:
|
|
//Base.loge(getNodeAsString(node));
|
|
if (((SingleVariableDeclaration) node).getName().toString().equalsIgnoreCase(name))
|
|
return node;
|
|
break;
|
|
case ASTNode.FIELD_DECLARATION:
|
|
//Base.loge("FD" + node);
|
|
vdfList = ((FieldDeclaration) node).fragments();
|
|
break;
|
|
case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
|
|
//Base.loge("VDE" + node);
|
|
vdfList = ((VariableDeclarationExpression) node).fragments();
|
|
break;
|
|
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
|
|
//Base.loge("VDS" + node);
|
|
vdfList = ((VariableDeclarationStatement) node).fragments();
|
|
break;
|
|
|
|
default:
|
|
|
|
}
|
|
if (vdfList != null) {
|
|
for (VariableDeclarationFragment vdf : vdfList) {
|
|
if (vdf.getName().toString().equalsIgnoreCase(name))
|
|
return node;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
static protected String getNodeAsString(ASTNode node) {
|
|
if (node == null)
|
|
return "NULL";
|
|
String className = node.getClass().getName();
|
|
int index = className.lastIndexOf(".");
|
|
if (index > 0)
|
|
className = className.substring(index + 1);
|
|
|
|
// if(node instanceof BodyDeclaration)
|
|
// return className;
|
|
|
|
String value = className;
|
|
|
|
if (node instanceof TypeDeclaration)
|
|
value = ((TypeDeclaration) node).getName().toString() + " | " + className;
|
|
else if (node instanceof MethodDeclaration)
|
|
value = ((MethodDeclaration) node).getName().toString() + " | "
|
|
+ className;
|
|
else if (node instanceof MethodInvocation)
|
|
value = ((MethodInvocation) node).getName().toString() + " | "
|
|
+ className;
|
|
else if (node instanceof FieldDeclaration)
|
|
value = node.toString() + " FldDecl | ";
|
|
else if (node instanceof SingleVariableDeclaration)
|
|
value = ((SingleVariableDeclaration) node).getName() + " - "
|
|
+ ((SingleVariableDeclaration) node).getType() + " | SVD ";
|
|
else if (node instanceof ExpressionStatement)
|
|
value = node.toString() + className;
|
|
else if (node instanceof SimpleName)
|
|
value = ((SimpleName) node).getFullyQualifiedName() + " | " + className;
|
|
else if (node instanceof QualifiedName)
|
|
value = node.toString() + " | " + className;
|
|
else if(node instanceof FieldAccess)
|
|
value = node.toString() + " | ";
|
|
else if (className.startsWith("Variable"))
|
|
value = node.toString() + " | " + className;
|
|
else if (className.endsWith("Type"))
|
|
value = node.toString() + " | " + className;
|
|
value += " [" + node.getStartPosition() + ","
|
|
+ (node.getStartPosition() + node.getLength()) + "]";
|
|
value += " Line: "
|
|
+ ((CompilationUnit) node.getRoot()).getLineNumber(node
|
|
.getStartPosition());
|
|
return value;
|
|
}
|
|
|
|
|
|
// public void jdocWindowVisible(boolean visible) {
|
|
// // frmJavaDoc.setVisible(visible);
|
|
// }
|
|
|
|
// public static String readFile2(String path) {
|
|
// BufferedReader reader = null;
|
|
// try {
|
|
// reader = new BufferedReader(
|
|
// new InputStreamReader(
|
|
// new FileInputStream(
|
|
// new File(
|
|
// path))));
|
|
// } catch (FileNotFoundException e) {
|
|
// e.printStackTrace();
|
|
// }
|
|
// try {
|
|
// StringBuilder ret = new StringBuilder();
|
|
// // ret.append("package " + className + ";\n");
|
|
// String line;
|
|
// while ((line = reader.readLine()) != null) {
|
|
// ret.append(line);
|
|
// ret.append("\n");
|
|
// }
|
|
// return ret.toString();
|
|
// } catch (IOException e) {
|
|
// e.printStackTrace();
|
|
// } finally {
|
|
// try {
|
|
// reader.close();
|
|
// } catch (IOException e) {
|
|
// e.printStackTrace();
|
|
// }
|
|
// }
|
|
// return null;
|
|
// }
|
|
|
|
|
|
static private void log(Object object) {
|
|
Messages.log(object == null ? "null" : object.toString());
|
|
}
|
|
|
|
|
|
/// Predictions --------------------------------------------------------------
|
|
|
|
|
|
protected static List<CompletionCandidate> trimCandidates(String newWord, List<CompletionCandidate> candidates) {
|
|
ArrayList<CompletionCandidate> newCandidate = new ArrayList<>();
|
|
newWord = newWord.toLowerCase();
|
|
for (CompletionCandidate comp : candidates) {
|
|
//if (comp.getNoHtmlLabel().toLowerCase().startsWith(newWord)) {
|
|
if (comp.startsWith(newWord)) {
|
|
newCandidate.add(comp);
|
|
}
|
|
}
|
|
return newCandidate;
|
|
}
|
|
|
|
protected List<CompletionCandidate> candidates;
|
|
protected String lastPredictedPhrase = " ";
|
|
|
|
/**
|
|
* The main function that calculates possible code completion candidates
|
|
*
|
|
* @param pdePhrase
|
|
* @param line
|
|
* @param lineStartNonWSOffset
|
|
*/
|
|
public List<CompletionCandidate> preparePredictions(final PreprocSketch ps,
|
|
final String pdePhrase,
|
|
final int lineNumber) {
|
|
Messages.log("* preparePredictions");
|
|
|
|
ASTNode astRootNode = (ASTNode) ps.compilationUnit.types().get(0);
|
|
|
|
// If the parsed code contains pde enhancements, take 'em out.
|
|
// TODO: test this
|
|
TextTransform transform = new TextTransform(pdePhrase);
|
|
transform.addAll(SourceUtil.replaceTypeConstructors(pdePhrase));
|
|
transform.addAll(SourceUtil.replaceHexLiterals(pdePhrase));
|
|
transform.addAll(SourceUtil.replaceColorRegex(pdePhrase));
|
|
transform.addAll(SourceUtil.fixFloatsRegex(pdePhrase));
|
|
String phrase = transform.apply();
|
|
|
|
//After typing 'arg.' all members of arg type are to be listed. This one is a flag for it
|
|
boolean noCompare = phrase.endsWith(".");
|
|
|
|
if (noCompare) {
|
|
phrase = phrase.substring(0, phrase.length() - 1);
|
|
}
|
|
|
|
boolean incremental = !noCompare &&
|
|
phrase.length() > lastPredictedPhrase.length() &&
|
|
phrase.startsWith(lastPredictedPhrase);
|
|
|
|
|
|
if (incremental) {
|
|
log(pdePhrase + " starts with " + lastPredictedPhrase);
|
|
log("Don't recalc");
|
|
|
|
if (phrase.contains(".")) {
|
|
int x = phrase.lastIndexOf('.');
|
|
candidates = trimCandidates(phrase.substring(x + 1), candidates);
|
|
} else {
|
|
candidates = trimCandidates(phrase, candidates);
|
|
}
|
|
lastPredictedPhrase = phrase;
|
|
return candidates;
|
|
}
|
|
|
|
// Ensure that we're not inside a comment. TODO: Binary search
|
|
|
|
/*for (Comment comm : getCodeComments()) {
|
|
int commLineNo = PdeToJavaLineNumber(compilationUnit
|
|
.getLineNumber(comm.getStartPosition()));
|
|
if(commLineNo == lineNumber){
|
|
log("Found a comment line " + comm);
|
|
log("Comment LSO "
|
|
+ javaCodeOffsetToLineStartOffset(compilationUnit
|
|
.getLineNumber(comm.getStartPosition()),
|
|
comm.getStartPosition()));
|
|
break;
|
|
}
|
|
}*/
|
|
|
|
// Now parse the expression into an ASTNode object
|
|
ASTNode nearestNode;
|
|
ASTParser parser = ASTParser.newParser(AST.JLS11);
|
|
parser.setKind(ASTParser.K_EXPRESSION);
|
|
parser.setSource(phrase.toCharArray());
|
|
ASTNode testnode = parser.createAST(null);
|
|
//Base.loge("PREDICTION PARSER PROBLEMS: " + parser);
|
|
// Find closest ASTNode of the document to this word
|
|
Messages.loge("Typed: " + phrase + "|" + " temp Node type: " + testnode.getClass().getSimpleName());
|
|
if(testnode instanceof MethodInvocation){
|
|
MethodInvocation mi = (MethodInvocation)testnode;
|
|
log(mi.getName() + "," + mi.getExpression() + "," + mi.typeArguments().size());
|
|
}
|
|
|
|
// find nearest ASTNode
|
|
nearestNode = findClosestNode(lineNumber, astRootNode);
|
|
if (nearestNode == null) {
|
|
// Make sure nearestNode is not NULL if couldn't find a closest node
|
|
nearestNode = astRootNode;
|
|
}
|
|
Messages.loge(lineNumber + " Nearest ASTNode to PRED "
|
|
+ getNodeAsString(nearestNode));
|
|
|
|
candidates = new ArrayList<>();
|
|
lastPredictedPhrase = phrase;
|
|
// Determine the expression typed
|
|
|
|
if (testnode instanceof SimpleName && !noCompare) {
|
|
Messages.loge("One word expression " + getNodeAsString(testnode));
|
|
//==> Simple one word exprssion - so is just an identifier
|
|
|
|
// Bottom up traversal of the AST to look for possible definitions at
|
|
// higher levels.
|
|
//nearestNode = nearestNode.getParent();
|
|
while (nearestNode != null) {
|
|
// If the current class has a super class, look inside it for
|
|
// definitions.
|
|
if (nearestNode instanceof TypeDeclaration) {
|
|
TypeDeclaration td = (TypeDeclaration) nearestNode;
|
|
if (td.getStructuralProperty(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY) != null) {
|
|
SimpleType st = (SimpleType) td.getStructuralProperty(TypeDeclaration.SUPERCLASS_TYPE_PROPERTY);
|
|
log("Superclass " + st.getName());
|
|
ArrayList<CompletionCandidate> tempCandidates =
|
|
getMembersForType(ps, st.getName().toString(), phrase, false, false);
|
|
for (CompletionCandidate can : tempCandidates) {
|
|
candidates.add(can);
|
|
}
|
|
//findDeclaration(st.getName())
|
|
}
|
|
}
|
|
List<StructuralPropertyDescriptor> sprops =
|
|
nearestNode.structuralPropertiesForType();
|
|
for (StructuralPropertyDescriptor sprop : sprops) {
|
|
ASTNode cnode;
|
|
if (!sprop.isChildListProperty()) {
|
|
if (nearestNode.getStructuralProperty(sprop) instanceof ASTNode) {
|
|
cnode = (ASTNode) nearestNode.getStructuralProperty(sprop);
|
|
CompletionCandidate[] types = checkForTypes(cnode);
|
|
if (types != null) {
|
|
for (CompletionCandidate type : types) {
|
|
if (type.getElementName().toLowerCase().startsWith(phrase.toLowerCase()))
|
|
candidates.add(type);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Childlist prop
|
|
List<ASTNode> nodelist =
|
|
(List<ASTNode>) nearestNode.getStructuralProperty(sprop);
|
|
for (ASTNode clnode : nodelist) {
|
|
CompletionCandidate[] types = checkForTypes(clnode);
|
|
if (types != null) {
|
|
for (CompletionCandidate type : types) {
|
|
if (type.getElementName().toLowerCase().startsWith(phrase.toLowerCase()))
|
|
candidates.add(type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nearestNode = nearestNode.getParent();
|
|
}
|
|
// We're seeing a simple name that's not defined locally or in
|
|
// the parent class. So most probably a pre-defined type.
|
|
log("Empty can. " + phrase);
|
|
ClassPath classPath = ps.classPath;
|
|
if (classPath != null) {
|
|
RegExpResourceFilter regExpResourceFilter =
|
|
new RegExpResourceFilter(Pattern.compile(".*"),
|
|
Pattern.compile(phrase + "[a-zA-Z_0-9]*.class",
|
|
Pattern.CASE_INSENSITIVE));
|
|
String[] resources = classPath.findResources("", regExpResourceFilter);
|
|
|
|
for (String matchedClass2 : resources) {
|
|
matchedClass2 = matchedClass2.replace('/', '.'); //package name
|
|
String matchedClass = matchedClass2.substring(0, matchedClass2.length() - 6);
|
|
int d = matchedClass.lastIndexOf('.');
|
|
if (!ignorableSuggestionImport(ps, matchedClass)) {
|
|
matchedClass = matchedClass.substring(d + 1); //class name
|
|
// display package name in grey
|
|
String html = "<html>" + matchedClass + " : <font color=#777777>" +
|
|
matchedClass2.substring(0, d) + "</font></html>";
|
|
candidates.add(new CompletionCandidate(matchedClass, html,
|
|
matchedClass,
|
|
CompletionCandidate.PREDEF_CLASS));
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// ==> Complex expression of type blah.blah2().doIt,etc
|
|
// Have to resolve it by carefully traversing AST of testNode
|
|
Messages.loge("Complex expression " + getNodeAsString(testnode));
|
|
log("candidates empty");
|
|
ASTNode childExpr = getChildExpression(testnode);
|
|
log("Parent expression : " + getParentExpression(testnode));
|
|
log("Child expression : " + childExpr);
|
|
if (!noCompare) {
|
|
log("Original testnode " + getNodeAsString(testnode));
|
|
testnode = getParentExpression(testnode);
|
|
log("Corrected testnode " + getNodeAsString(testnode));
|
|
}
|
|
ClassMember expr =
|
|
resolveExpression3rdParty(ps, nearestNode, testnode, noCompare);
|
|
if (expr == null) {
|
|
log("Expr is null");
|
|
} else {
|
|
boolean isArray = expr.thisclass != null && expr.thisclass.isArray();
|
|
boolean isSimpleType = (expr.astNode != null) &&
|
|
expr.astNode.getNodeType() == ASTNode.SIMPLE_TYPE;
|
|
boolean isMethod = expr.method != null;
|
|
boolean staticOnly = !isMethod && !isArray && !isSimpleType;
|
|
log("Expr is " + expr.toString());
|
|
String lookFor = (noCompare || (childExpr == null)) ?
|
|
"" : childExpr.toString();
|
|
candidates = getMembersForType(ps, expr, lookFor, noCompare, staticOnly);
|
|
}
|
|
}
|
|
return candidates;
|
|
}
|
|
|
|
|
|
protected static DefaultListModel<CompletionCandidate> filterPredictions(List<CompletionCandidate> candidates) {
|
|
Messages.log("* filterPredictions");
|
|
DefaultListModel<CompletionCandidate> defListModel = new DefaultListModel<>();
|
|
if (candidates.isEmpty())
|
|
return defListModel;
|
|
// check if first & last CompCandidate are the same methods, only then show all overloaded methods
|
|
if (candidates.get(0).getElementName()
|
|
.equals(candidates.get(candidates.size() - 1).getElementName())) {
|
|
log("All CC are methods only: " + candidates.get(0).getElementName());
|
|
for (int i = 0; i < candidates.size(); i++) {
|
|
CompletionCandidate cc = candidates.get(i).withRegeneratedCompString();
|
|
candidates.set(i, cc);
|
|
defListModel.addElement(cc);
|
|
}
|
|
}
|
|
else {
|
|
boolean ignoredSome = false;
|
|
for (int i = 0; i < candidates.size(); i++) {
|
|
if(i > 0 && (candidates.get(i).getElementName()
|
|
.equals(candidates.get(i - 1).getElementName()))){
|
|
if (candidates.get(i).getType() == CompletionCandidate.LOCAL_METHOD
|
|
|| candidates.get(i).getType() == CompletionCandidate.PREDEF_METHOD) {
|
|
CompletionCandidate cc = candidates.get(i - 1);
|
|
String label = cc.getLabel();
|
|
int x = label.lastIndexOf(')');
|
|
String newLabel;
|
|
if (candidates.get(i).getType() == CompletionCandidate.PREDEF_METHOD) {
|
|
newLabel = (cc.getLabel().contains("<html>") ? "<html>" : "")
|
|
+ cc.getElementName() + "(...)" + label.substring(x + 1);
|
|
} else {
|
|
newLabel = cc.getElementName() + "(...)" + label.substring(x + 1);
|
|
}
|
|
String newCompString = cc.getElementName() + "(";
|
|
candidates.set(i - 1, cc.withLabelAndCompString(newLabel, newCompString));
|
|
ignoredSome = true;
|
|
continue;
|
|
}
|
|
}
|
|
defListModel.addElement(candidates.get(i));
|
|
}
|
|
if (ignoredSome) {
|
|
log("Some suggestions hidden");
|
|
}
|
|
}
|
|
return defListModel;
|
|
}
|
|
|
|
|
|
|
|
/// JavaDocs -----------------------------------------------------------------
|
|
|
|
//TODO: Work on this later.
|
|
/*
|
|
protected TreeMap<String, String> jdocMap;
|
|
|
|
|
|
protected void loadJavaDoc() {
|
|
jdocMap = new TreeMap<>();
|
|
|
|
// presently loading only p5 reference for PApplet
|
|
// TODO: use something like ExecutorService here [jv]
|
|
new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
try {
|
|
loadJavaDoc(jdocMap, editor.getMode().getReferenceFolder());
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}).start();
|
|
}
|
|
|
|
|
|
static void loadJavaDoc(TreeMap<String, String> jdocMap,
|
|
File referenceFolder) throws IOException {
|
|
Document doc;
|
|
|
|
FileFilter fileFilter = new FileFilter() {
|
|
@Override
|
|
public boolean accept(File file) {
|
|
if(!file.getName().endsWith("_.html"))
|
|
return false;
|
|
int k = 0;
|
|
for (int i = 0; i < file.getName().length(); i++) {
|
|
if(file.getName().charAt(i)== '_')
|
|
k++;
|
|
if(k > 1)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
for (File docFile : referenceFolder.listFiles(fileFilter)) {
|
|
doc = Jsoup.parse(docFile, null);
|
|
Elements elm = doc.getElementsByClass("ref-item");
|
|
String msg = "";
|
|
String methodName = docFile.getName().substring(0, docFile.getName().indexOf('_'));
|
|
//System.out.println(methodName);
|
|
for (org.jsoup.nodes.Element ele : elm) {
|
|
msg = "<html><body> <strong><div style=\"width: 300px; text-justification: justify;\"></strong><table cellpadding=\"0\" cellspacing=\"0\" border=\"0\" class=\"ref-item\">"
|
|
+ ele.html() + "</table></div></html></body></html>";
|
|
//mat.replaceAll("");
|
|
msg = msg.replaceAll("img src=\"", "img src=\""
|
|
+ referenceFolder.toURI().toURL().toString() + "/");
|
|
//System.out.println(ele.text());
|
|
}
|
|
jdocMap.put(methodName, msg);
|
|
}
|
|
//System.out.println("JDoc loaded " + jdocMap.size());
|
|
}
|
|
|
|
|
|
public void updateJavaDoc(final CompletionCandidate candidate) {
|
|
String methodmatch = candidate.toString();
|
|
if (methodmatch.indexOf('(') != -1) {
|
|
methodmatch = methodmatch.substring(0, methodmatch.indexOf('('));
|
|
}
|
|
|
|
//log("jdoc match " + methodmatch);
|
|
String temp = "<html> </html>";
|
|
for (final String key : jdocMap.keySet()) {
|
|
if (key.startsWith(methodmatch) && key.length() > 3) {
|
|
log("Matched jdoc " + key);
|
|
if (candidate.getWrappedObject() != null) {
|
|
String definingClass = "";
|
|
if (candidate.getWrappedObject() instanceof Field)
|
|
definingClass = ((Field) candidate.getWrappedObject())
|
|
.getDeclaringClass().getName();
|
|
else if (candidate.getWrappedObject() instanceof Method)
|
|
definingClass = ((Method) candidate.getWrappedObject())
|
|
.getDeclaringClass().getName();
|
|
if (definingClass.equals("processing.core.PApplet")) {
|
|
temp = (jdocMap.get(key));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
final String jdocString = temp;
|
|
SwingUtilities.invokeLater(new Runnable() {
|
|
public void run() {
|
|
javadocPane.setText(jdocString);
|
|
scrollPane.getVerticalScrollBar().setValue(0);
|
|
//frmJavaDoc.setVisible(!jdocString.equals("<html> </html>"));
|
|
editor.toFront();
|
|
editor.ta.requestFocus();
|
|
}
|
|
});
|
|
}
|
|
*/
|
|
|
|
}
|