/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.lang.psi.impl.resolvers;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.impl.PsiDocumentManagerImpl;
import com.intellij.psi.impl.PsiManagerImpl;
import gw.config.CommonServices;
import gw.fs.IFile;
import gw.lang.parser.IFunctionSymbol;
import gw.lang.parser.IReducedSymbol;
import gw.lang.reflect.*;
import gw.lang.reflect.gs.IGosuClass;
import gw.plugin.ij.filesystem.IDEAResource;
import gw.plugin.ij.lang.psi.api.IFeatureResolver;
import gw.plugin.ij.lang.psi.api.IGosuResolveResult;
import gw.plugin.ij.lang.psi.impl.expressions.GosuReferenceExpressionImpl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PsiFeatureResolver {
private static final Logger LOG = Logger.getInstance(PsiFeatureResolver.class);
@Nullable
public static PsiElement resolveMethodOrConstructor(@NotNull IHasParameterInfos info, @NotNull PsiElement context) {
final IGosuResolveResult result = resolveMethodOrConstructorWithSubstitutor(info, context);
return result != null ? result.getElement() : null;
}
@Nullable
public static IGosuResolveResult resolveMethodOrConstructorWithSubstitutor(@NotNull IHasParameterInfos info, @NotNull PsiElement context) {
if (info instanceof IMethodInfoDelegate) {
return resolveMethodOrConstructorWithSubstitutor(((IMethodInfoDelegate) info).getSource(), context);
} else {
for (IFeatureResolver resolver : FeatureResolverExtensionBean.getResolvers()) {
final IGosuResolveResult element = resolver.resolveMethodOrConstructor(info, context);
if (element != null) {
return element;
}
}
return null;
}
}
@Nullable
public static PsiElement resolveMethodOrConstructor(@NotNull IFunctionSymbol symbol, @NotNull PsiElement context) {
for (IFeatureResolver resolver : FeatureResolverExtensionBean.getResolvers()) {
final PsiElement element = resolver.resolveMethodOrConstructor( symbol, context );
if (element != null) {
return element;
}
}
return null;
}
@Nullable
public static PsiElement resolveProperty(@NotNull IPropertyInfo pi, PsiElement ctx) {
PsiElement result;
while ( true ) {
result = resolvePropertyWithoutDelegating( pi, ctx );
if ( result == null && pi instanceof IPropertyInfoDelegate ) {
IPropertyInfoDelegate propertyInfoDelegate = (IPropertyInfoDelegate) pi;
pi = propertyInfoDelegate.getSource();
}
else {
return result;
}
}
}
@Nullable
private static PsiElement resolvePropertyWithoutDelegating(@NotNull IPropertyInfo pi, PsiElement ctx) {
if (!isResolvable(pi)) {
return null;
}
for (IFeatureResolver resolver : FeatureResolverExtensionBean.getResolvers()) {
final PsiElement element = resolver.resolve(pi, ctx);
if (element != null) {
return element;
}
}
return null;
}
private static boolean isResolvable(@NotNull IPropertyInfo pi) {
if (pi instanceof ILocationAwareFeature) {
return true;
}
if (!(pi instanceof IFileBasedFeature)) {
final IType ownersType = pi.getOwnersType();
if (ownersType == null || ownersType instanceof IErrorType) {
return false;
}
if (!(ownersType instanceof IFileBasedType)) {
// LOG.warn("Reference resolution is only supported for file-based types: " + ownersType.getClass().getName());
return false;
}
}
return true;
}
@Nullable
public static PsiElement resolveSymbol(IReducedSymbol symbol, IGosuClass gsClass, GosuReferenceExpressionImpl context) {
for (IFeatureResolver resolver : FeatureResolverExtensionBean.getResolvers()) {
final PsiElement element = resolver.resolve(symbol, gsClass, context);
if (element != null) {
return element;
}
}
return null;
}
public static PsiElement resolveFeatureAtLocation( PsiElement context, LocationInfo location ) {
if ( location == null ) {
return null;
}
final IFile file = CommonServices.getFileSystem().getIFile(location.getFileUrl());
Project project = context.getProject();
VirtualFile vfile = ((IDEAResource) file ).getVirtualFile();
if (vfile == null) {
return null;
}
final PsiFile psiFile = PsiManagerImpl.getInstance(project).findFile( vfile );
final Document document = PsiDocumentManagerImpl.getInstance(project).getDocument( psiFile );
final int offset = document.getLineStartOffset( location.getLineNumber() - 1 ) + location.getColumnNumber() - 2;
PsiElement element = psiFile.findElementAt( offset ); // empty element end
element = element.getParent(); // element definition that contains the empty element end
return element;
}
}