/* * Copyright 2009-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.codehaus.jdt.groovy.integration.internal; import org.codehaus.groovy.ast.AnnotationNode; import org.codehaus.groovy.ast.ClassCodeVisitorSupport; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.FieldNode; import org.codehaus.groovy.ast.GenericsType; import org.codehaus.groovy.ast.ImportNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.ModuleNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.ast.expr.CastExpression; import org.codehaus.groovy.ast.expr.ClassExpression; import org.codehaus.groovy.ast.expr.ClosureExpression; import org.codehaus.groovy.ast.expr.ConstantExpression; import org.codehaus.groovy.ast.expr.ConstructorCallExpression; import org.codehaus.groovy.ast.expr.DeclarationExpression; import org.codehaus.groovy.ast.expr.FieldExpression; import org.codehaus.groovy.ast.expr.MethodCallExpression; import org.codehaus.groovy.ast.expr.VariableExpression; import org.codehaus.groovy.ast.stmt.Statement; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.groovy.core.Activator; import org.eclipse.jdt.groovy.core.util.GroovyUtils; import org.eclipse.jdt.internal.compiler.ISourceElementRequestor; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.core.util.Util; /** * @author Andrew Eisenberg * @created Aug 27, 2009 * * Visits a ModuleNode and passes it to an indexing element requestor, thus adding this class to the Java indexes */ public class GroovyIndexingVisitor extends ClassCodeVisitorSupport { private ISourceElementRequestor requestor; // used for GRECLIPSE-741, remove when issue is solved private ModuleNode module; public GroovyIndexingVisitor(ISourceElementRequestor requestor) { this.requestor = requestor; } void doVisit(ModuleNode node, ImportReference pkg) { if (node == null) { // there is an unrecoverable compile problem. return; } // used for GRECLIPSE-741, remove when issue is solved module = node; try { this.visitImports(node); for (ClassNode clazz : (Iterable<ClassNode>) node.getClasses()) { this.visitClass(clazz); } } catch (RuntimeException e) { Util.log(e); } } public void visitImports(ModuleNode node) { if (node != null) { for (ImportNode importNode : GroovyUtils.getAllImportNodes(node)) { visitAnnotations(importNode); if (importNode.getType() != null) { handleType(importNode.getType(), false, true); } String importFieldName = importNode.getFieldName(); if (importFieldName != null) { requestor.acceptUnknownReference(importFieldName.toCharArray(), 0); } } } } @Override public void visitMethodCallExpression(MethodCallExpression call) { super.visitMethodCallExpression(call); String methodStr = call.getMethodAsString(); if (methodStr == null) return; char[] methodName = methodStr.toCharArray(); int start = call.getStart(); // also could be a field reference requestor.acceptFieldReference(methodName, start); // we don't know how many arguments the method has, so go up to 7. for (int i = 0; i < 7; i++) { requestor.acceptMethodReference(methodName, i, start); } } @Override public void visitFieldExpression(FieldExpression expression) { super.visitFieldExpression(expression); requestor.acceptFieldReference(expression.getFieldName().toCharArray(), expression.getStart()); } @Override public void visitConstantExpression(ConstantExpression expression) { if (!(expression.isTrueExpression() || expression.isFalseExpression() || expression.isNullExpression() || expression .isEmptyStringExpression())) { char[] constName = expression.getValue().toString().toCharArray(); int start = expression.getStart(); requestor.acceptFieldReference(constName, start); // also could be a method reference // we don't know how many arguments the method has, so go up to 7. for (int i = 0; i < 7; i++) { requestor.acceptMethodReference(constName, i, start); } } super.visitConstantExpression(expression); } @Override public void visitCastExpression(CastExpression expression) { handleType(expression.getType(), false, true); } @Override public void visitClassExpression(ClassExpression expression) { handleType(expression.getType(), false, true); } @Override public void visitConstructorCallExpression(ConstructorCallExpression call) { super.visitConstructorCallExpression(call); for (int i = 0; i < 10; i++) { requestor.acceptConstructorReference(call.getType().getName().toCharArray(), i, call.getStart()); } // handleType(call.getType(), false); } @Override public void visitDeclarationExpression(DeclarationExpression expression) { handleType(expression.getLeftExpression().getType(), false, true); expression.getRightExpression().visit(this); // super.visitDeclarationExpression(expression); } @Override public void visitVariableExpression(VariableExpression expression) { requestor.acceptUnknownReference(expression.getName().toCharArray(), expression.getStart()); } @Override public void visitField(FieldNode node) { // handleType(node.getType(), false); super.visitField(node); } @Override public void visitMethod(MethodNode node) { if (!node.isSynthetic()) { handleType(node.getReturnType(), false, true); for (Parameter param : node.getParameters()) { handleType(param.getType(), false, true); } } super.visitMethod(node); } @Override public void visitClass(ClassNode node) { if (!node.isSynthetic()) { handleType(node, false, false); handleType(node.getSuperClass(), false, true); for (ClassNode impls : node.getInterfaces()) { handleType(impls, false, true); } // handleType(node, node.isAnnotationDefinition(), false); } // don't do a super call because it revisits the imports and packages // super.visitClass(node); visitAnnotations(node); node.visitContents(this); for (Statement element : (Iterable<Statement>) node.getObjectInitializerStatements()) { element.visit(this); } } @Override public void visitClosureExpression(ClosureExpression node) { if (node.getParameters() != null) { for (Parameter param : node.getParameters()) { handleType(param.getType(), false, true); } } super.visitClosureExpression(node); } @Override protected void visitAnnotation(AnnotationNode node) { handleType(node.getClassNode(), true, true); super.visitAnnotation(node); } // may not be resolved private void handleType(ClassNode node, boolean isAnnotation, boolean useQualifiedName) { if (node == null) { // GRECLIPSE-741 Util.log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, "GRECLIPSE-741: module: " + module.getDescription(), new RuntimeException())); return; } if (isAnnotation) { requestor.acceptAnnotationTypeReference(splitName(node, useQualifiedName), node.getStart(), node.getEnd()); } else { ClassNode componentType = node.getComponentType(); requestor.acceptTypeReference(splitName(componentType != null ? componentType : node, useQualifiedName), node.getStart(), node.getEnd()); } if (node.isUsingGenerics() && node.getGenericsTypes() != null) { for (GenericsType gen : node.getGenericsTypes()) { ClassNode lowerBound = gen.getLowerBound(); if (lowerBound != null) { handleType(lowerBound, lowerBound.isAnnotationDefinition(), true); } if (gen.getUpperBounds() != null) { for (ClassNode upper : gen.getUpperBounds()) { // handle enums where the upper bound is the same as the type if (!upper.getName().equals(node.getName())) { handleType(upper, upper.isAnnotationDefinition(), true); } } } ClassNode genType = gen.getType(); if (genType != null && gen.getName().charAt(0) != '?') { handleType(genType, genType.isAnnotationDefinition(), true); } } } } private char[][] splitName(ClassNode node, boolean useQualifiedName) { String name = useQualifiedName ? node.getName() : node.getNameWithoutPackage(); String[] nameArr = name.split("\\."); char[][] nameCharArr = new char[nameArr.length][]; for (int i = 0; i < nameArr.length; i++) { nameCharArr[i] = nameArr[i].toCharArray(); } return nameCharArr; } }