/*
* 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.groovy.eclipse.core.model;
import static org.codehaus.groovy.eclipse.core.util.ListUtil.newList;
import java.util.LinkedList;
import java.util.List;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.InnerClassNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.Parameter;
import org.codehaus.groovy.ast.expr.DeclarationExpression;
import org.codehaus.groovy.eclipse.core.GroovyCore;
import org.codehaus.jdt.groovy.model.GroovyCompilationUnit;
import org.codehaus.jdt.groovy.model.GroovyNature;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.internal.core.JavaElement;
import org.eclipse.jdt.internal.core.LocalVariable;
import org.eclipse.jdt.internal.core.SourceType;
/**
* @author Andrew Eisenberg
* @created May 29, 2009
*
* This class provides some useful methods for accessing Groovy state
*/
public class GroovyProjectFacade {
public static boolean isGroovyProject(IProject proj) {
try {
return proj.hasNature(GroovyNature.GROOVY_NATURE);
} catch (CoreException e) {
GroovyCore.logException("Error getting project nature: " + proj.getName(), e);
return false;
}
}
private IJavaProject project;
public GroovyProjectFacade(IJavaProject project) {
this.project = project;
}
public GroovyProjectFacade(IJavaElement elt) {
this.project = elt.getJavaProject();
}
/**
* best effort to map a groovy node to a JavaElement
* If the groovy element is not a declaration (eg- an expression or statement)
* returns instead the closest enclosing element that can be converted
* to an IJavaElement
*
* If node is a local variable declaration, then returns a LocalVariable
*/
public IJavaElement groovyNodeToJavaElement(ASTNode node, IFile file) {
ICompilationUnit unit = JavaCore.createCompilationUnitFrom(file);
if (! (unit instanceof GroovyCompilationUnit)) {
// can't go any further, just return the unit instead
GroovyCore.logWarning("Trying to get a groovy element from a non-groovy file: " + file.getName());
return unit;
}
try {
int start = node.getStart();
IJavaElement elt = unit.getElementAt(start);
if (node instanceof DeclarationExpression) {
String var = ((DeclarationExpression) node).getVariableExpression().getName();
int end = start + var.length() - 1;
String sig = Signature.createTypeSignature(((DeclarationExpression) node).getVariableExpression().getType().getName(), false);
return new LocalVariable((JavaElement) elt, var, start, end, start, end, sig, null, 0, false);
} else {
return elt;
}
} catch (Exception e) {
GroovyCore.logException("Error converting from Groovy Element to Java Element: " + node.getText(), e);
}
return null;
}
public IType groovyClassToJavaType(ClassNode node) {
try {
// GRECLIPSE-1628 handle anonymous inner classes. Only go one level
// deep
// FIXADE ignore rest
ClassNode toLookFor = node;
if (node.getEnclosingMethod() != null) {
toLookFor = node.getEnclosingMethod().getDeclaringClass();
IType enclosing = groovyClassToJavaType(node.getEnclosingMethod().getDeclaringClass());
if (enclosing != null) {
if (!enclosing.isBinary()) {
// Throws exception enclosing is a binary type!
return findAnonymousInnerClass(enclosing, (InnerClassNode) node);
} else {
// If the 'enclosing' is binary we may assume this one
// is also binary and so we should
// just be able to look for it with binary type name,
// (including the $ etc.)
return project.findType(node.getName(), new NullProgressMonitor());
}
} else {
return null;
}
}
// GRECLIPSE-800 Ensure that inner class nodes are handled properly
String name = toLookFor.getName().replace('$', '.');
IType type = project.findType(name, new NullProgressMonitor());
if (type != null && toLookFor != node) {
type = type.getType("", 1);
if (!type.exists()) {
type = null;
}
}
return type;
} catch (JavaModelException e) {
GroovyCore.logException("Error converting from Groovy Element to Java Element: " + node.getName(), e);
return null;
}
}
private IType findAnonymousInnerClass(IType enclosing, InnerClassNode anon) throws JavaModelException {
IMethod[] children = enclosing.getMethods();
MethodNode enclosingMethod = anon.getEnclosingMethod();
if (enclosingMethod == null) {
return null;
}
Parameter[] parameters = enclosingMethod.getParameters();
if (parameters == null) {
parameters = Parameter.EMPTY_ARRAY;
}
for (IMethod child : children) {
if (child.getElementName().equals(enclosingMethod.getName())) {
String[] names = child.getParameterNames();
if (names.length == parameters.length) {
// FIXADE for now, only look for a single inner type
// check the names, not the types since we don't know the
// full types of the IMethod.
// This could go wrong, but it's safe enoug
for (int i = 0; i < names.length; i++) {
if (!names[i].equals(parameters[i].getName())) {
continue;
}
}
IType found = child.getType("", 1);
if (found.exists()) {
return found;
} else {
return null;
}
}
}
}
return null;
}
GroovyCompilationUnit groovyModuleToCompilationUnit(ModuleNode node) {
List<ClassNode> classes = node.getClasses();
ClassNode classNode = classes.size() > 0 ? (ClassNode) classes.get(0) : null;
if (classNode != null) {
IType type = groovyClassToJavaType(classNode);
if (type instanceof SourceType) {
return (GroovyCompilationUnit) type.getCompilationUnit();
}
}
GroovyCore.logWarning("Trying to get GroovyCompilationUnit for non-groovy module: " + node.getDescription());
return null;
}
/**
* If this fully qualified name is in a groovy file, then return the
* classnode
*
* If this is not a groovy file, then return null
*
* @param name
* @return
*/
public ClassNode getClassNodeForName(String name) {
try {
IType type = project.findType(name, new NullProgressMonitor());
if (type instanceof SourceType) {
return javaTypeToGroovyClass(type);
}
} catch (JavaModelException e) {
GroovyCore.logException(e.getMessage(), e);
}
return null;
}
private ClassNode javaTypeToGroovyClass(IType type) {
ICompilationUnit unit = type.getCompilationUnit();
if (unit instanceof GroovyCompilationUnit) {
ModuleNode module = ((GroovyCompilationUnit) unit).getModuleNode();
List<ClassNode> classes = module.getClasses();
for (ClassNode classNode : classes) {
if (classNode.getNameWithoutPackage().equals(type.getElementName())) {
return classNode;
}
}
}
return null;
}
public List<IType> findAllRunnableTypes() throws JavaModelException {
final List<IType> results = newList();
IPackageFragmentRoot[] roots = project.getAllPackageFragmentRoots();
for (IPackageFragmentRoot root : roots) {
if (!root.isReadOnly()) {
IJavaElement[] children = root.getChildren();
for (IJavaElement child : children) {
if (child.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
ICompilationUnit[] units = ((IPackageFragment) child).getCompilationUnits();
for (ICompilationUnit unit : units) {
results.addAll(findAllRunnableTypes(unit));
}
}
}
}
}
return results;
}
public static List<IType> findAllRunnableTypes(ICompilationUnit unit) throws JavaModelException {
List<IType> results = new LinkedList<IType>();
IType[] types = unit.getAllTypes();
for (IType type : types) {
if (hasRunnableMain(type)) {
results.add(type);
}
}
return results;
}
public static boolean hasRunnableMain(IType type) {
try {
IMethod[] allMethods = type.getMethods();
for (IMethod method : allMethods) {
if (method.getElementName().equals("main") &&
Flags.isStatic(method.getFlags()) &&
// void or Object are valid return types
(method.getReturnType().equals("V") ||
method.getReturnType().endsWith("java.lang.Object;")) &&
hasAppropriateArrayArgsForMain(method.getParameterTypes())) {
return true;
}
}
} catch (JavaModelException e) {
GroovyCore.logException("Exception searching for main method in " + type, e);
}
return false;
}
private static boolean hasAppropriateArrayArgsForMain(
final String[] params) {
if (params == null || params.length != 1) {
return false;
}
int array = Signature.getArrayCount(params[0]);
String typeName;
if (array == 1) {
typeName = "String";
} else if (array == 0) {
typeName = "Object";
} else {
return false;
}
String sigNoArray = Signature.getElementType(params[0]);
String name = Signature.getSignatureSimpleName(sigNoArray);
String qual = Signature.getSignatureQualifier(sigNoArray);
return (name.equals(typeName)) &&
(qual == null || qual.equals("java.lang") || qual.equals(""));
}
public IJavaProject getProject() {
return project;
}
public boolean isGroovyScript(IType type) {
ClassNode node = javaTypeToGroovyClass(type);
if (node != null) {
return node.isScript();
}
return false;
}
public List<IType> findAllScripts() throws JavaModelException {
final List<IType> results = newList();
IPackageFragmentRoot[] roots = project.getAllPackageFragmentRoots();
for (IPackageFragmentRoot root : roots) {
if (!root.isReadOnly()) {
IJavaElement[] children = root.getChildren();
for (IJavaElement child : children) {
if (child.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
ICompilationUnit[] units = ((IPackageFragment) child).getCompilationUnits();
for (ICompilationUnit unit : units) {
if (unit instanceof GroovyCompilationUnit) {
for (IType type : unit.getTypes()) {
if (isGroovyScript(type)) {
results.add(type);
}
}
}
}
}
}
}
}
return results;
}
public boolean isGroovyScript(ICompilationUnit unit) {
if (unit instanceof GroovyCompilationUnit) {
GroovyCompilationUnit gunit = (GroovyCompilationUnit) unit;
ModuleNode module = gunit.getModuleNode();
if (module != null) {
for (ClassNode clazz : (Iterable<ClassNode>) module.getClasses()) {
if (clazz.isScript()) {
return true;
}
}
}
}
return false;
}
}