processing.mode.java.pdex.CompletionGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-mode Show documentation
Show all versions of java-mode Show documentation
Processing is a programming language, development environment, and online community.
This Java Mode package contains the Java mode for Processing IDE.
/* -*- 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.pdex;
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 processing.mode.java.JavaMode;
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 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 params = (List) 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(PreprocessedSketch 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 getMembersForType(PreprocessedSketch ps,
String typeName,
String child,
boolean noCompare,
boolean staticOnly) {
ArrayList 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 getMembersForType(PreprocessedSketch ps,
ClassMember tehClass,
String childToLookFor,
boolean noCompare,
boolean staticOnly) {
String child = childToLookFor.toLowerCase();
ArrayList 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 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 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 = "clone() : " + className +
" - " + className + "";
candidates.add(new CompletionCandidate("clone()", methodLabel, "clone()",
CompletionCandidate.PREDEF_METHOD));
}
if ("length".startsWith(child)) {
String fieldLabel = "length : int - " +
className + "";
candidates.add(new CompletionCandidate("length", fieldLabel, "length",
CompletionCandidate.PREDEF_FIELD));
}
}
return candidates;
}
private static boolean isStatic(List 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(PreprocessedSketch 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 programImports = ps.programImports;
List codeFolderImports = ps.codeFolderImports;
List coreAndDefaultImports = ps.coreAndDefaultImports;
ImportStatement javaLang = ImportStatement.wholePackage("java.lang");
Stream> 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.getClassName().equals(finalClassName)) {
return is.getFullClassName();
} 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(PreprocessedSketch 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(PreprocessedSketch 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 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) 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 nodelist = (List) 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 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 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 nodelist = (List) 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 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 nodelist = (List) 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(PreprocessedSketch ps, String impName) {
String impNameLc = impName.toLowerCase();
List programImports = ps.programImports;
List 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(PreprocessedSketch 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 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 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 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 trimCandidates(String newWord, List candidates) {
ArrayList newCandidate = new ArrayList<>();
newWord = newWord.toLowerCase();
for (CompletionCandidate comp : candidates) {
if(comp.getNoHtmlLabel().toLowerCase().startsWith(newWord)){
newCandidate.add(comp);
}
}
return newCandidate;
}
protected List candidates;
protected String lastPredictedPhrase = " ";
/**
* The main function that calculates possible code completion candidates
*
* @param pdePhrase
* @param line
* @param lineStartNonWSOffset
*/
public List preparePredictions(final PreprocessedSketch 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(SourceUtils.replaceTypeConstructors(pdePhrase));
transform.addAll(SourceUtils.replaceHexLiterals(pdePhrase));
transform.addAll(SourceUtils.replaceColorRegex(pdePhrase));
transform.addAll(SourceUtils.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.JLS8);
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 tempCandidates =
getMembersForType(ps, st.getName().toString(), phrase, false, false);
for (CompletionCandidate can : tempCandidates) {
candidates.add(can);
}
//findDeclaration(st.getName())
}
}
List 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 nodelist =
(List) 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 = "" + matchedClass + " : " +
matchedClass2.substring(0, d) + "";
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 filterPredictions(List candidates) {
Messages.log("* filterPredictions");
DefaultListModel 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("") ? "" : "")
+ 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 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 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 = " "
+ ele.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 = " ";
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(" "));
editor.toFront();
editor.ta.requestFocus();
}
});
}
*/
}