package org.jetbrains.plugins.ruby.motion;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.ruby.motion.bridgesupport.Class;
import org.jetbrains.plugins.ruby.motion.bridgesupport.*;
import org.jetbrains.plugins.ruby.motion.symbols.*;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.RubySymbolProviderBase;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.Type;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.TypeSet;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.fqn.FQN;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.Symbol;
import org.jetbrains.plugins.ruby.ruby.codeInsight.symbols.structure.SymbolUtil;
import org.jetbrains.plugins.ruby.ruby.codeInsight.types.CoreTypes;
import org.jetbrains.plugins.ruby.ruby.lang.psi.RFile;
import org.jetbrains.plugins.ruby.ruby.lang.psi.controlStructures.classes.RClass;
import org.jetbrains.plugins.ruby.ruby.lang.psi.controlStructures.modules.RModule;
import org.jetbrains.plugins.ruby.ruby.lang.psi.holders.RContainer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* @author Dennis.Ushakov
*/
public class RubyMotionSymbolProvider extends RubySymbolProviderBase {
@Override
public Symbol findSymbol(@NotNull Symbol anchor,
@NotNull FQN fqn,
TypeSet types,
@Nullable PsiElement invocationPoint) {
if (!RubyMotionUtil.getInstance().hasMacRubySupport(invocationPoint)) return null;
final Module module = getModule(invocationPoint);
if (module == null) return null;
final Collection<Framework> frameworks = ((RubyMotionUtilImpl)RubyMotionUtil.getInstance()).getFrameworks(module);
final List<String> nameAsList = fqn.asList();
if (nameAsList.isEmpty()) {
return null;
}
final String name = nameAsList.get(0);
if (name.isEmpty()) return null;
if (types.contains(Type.CLASS)) {
Symbol result = findClassOrStruct(module, frameworks, nameAsList);
if (result != null) {
return result;
}
}
if (types.contains(Type.CLASS_METHOD)) {
for (Framework framework : frameworks) {
Function function = framework.getFunction(name);
final String original = framework.getOriginalFunctionName(name);
if (original != null) {
function = framework.getFunction(original);
}
if (function != null) {
return MotionSymbolUtil.createFunctionSymbol(module, null, function);
}
}
}
if (types.contains(Type.CONSTANT)) {
for (Framework framework : frameworks) {
Constant constant = findConstant(name, framework);
if (constant != null) {
return MotionSymbolUtil.createConstantSymbol(module, constant);
}
}
}
return null;
}
public static Constant findConstant(String name, Framework framework) {
Constant constant = framework.getConstant(name);
if (constant == null) {
constant = framework.getConstant(Character.toLowerCase(name.charAt(0)) + name.substring(1));
}
return constant;
}
@Nullable
public static Symbol findClassOrStruct(Module module, Collection<Framework> frameworks, List<String> name) {
final List<Class> classes = new ArrayList<>();
for (Framework framework : frameworks) {
final Class clazz = framework.getClass(name);
ContainerUtil.addIfNotNull(classes, clazz);
}
if (!classes.isEmpty()) {
return new MotionClassSymbol(module, classes);
}
if (name.size() > 1) return null;
for (Framework framework : frameworks) {
final Struct struct = framework.getStruct(name.get(0));
if (struct != null) {
return new StructSymbol(module, struct);
}
}
return null;
}
@Nullable
private static Module getModule(@Nullable PsiElement invocationPoint) {
return invocationPoint != null ? ModuleUtilCore.findModuleForPsiElement(invocationPoint.getContainingFile()) : null;
}
@Override
public Symbol createSymbolByContainer(@NotNull RContainer container,
@NotNull FQN fqn,
@Nullable Symbol parent) {
if (!RubyMotionUtil.getInstance().hasMacRubySupport(container)) return null;
final Module module = getModule(container);
if (module == null) return null;
final Symbol motionSymbol = getCachedSpecificSymbol(container, container);
if (motionSymbol != null) {
return motionSymbol;
}
return null;
}
@Nullable
@Override
protected Symbol getSpecificSymbol(PsiElement element, RContainer context) {
if (context instanceof RClass || context instanceof RModule) {
final boolean isObject = CoreTypes.Object.equals(context.getFQN().getFullPath());
final RubyMotionSymbol symbol = isObject ? new RubyMotionSymbol((RFile)context.getContainingFile()) : null;
final Symbol nsObject = SymbolUtil.findSymbol(element.getProject(), Type.CLASS, getParentName(context), element);
final List<Symbol> includes = isObject ? Collections.singletonList(symbol) : Collections.emptyList();
return new MotionEnabledClassModuleSymbol(context, includes, Collections.emptyList(),
Collections.singletonList(nsObject));
}
return null;
}
private static String getParentName(RContainer context) {
final String name = context.getName();
if (CoreTypes.String.equals(name)) {
return "NSMutableString";
}
if (CoreTypes.Array.equals(name)) {
return "NSMutableArray";
}
if (CoreTypes.Hash.equals(name)) {
return "NSMutableDictionary";
}
if (CoreTypes.Numeric.equals(name)) {
return "NSNumber";
}
if (CoreTypes.Time.equals(name)) {
return "NSDate";
}
return "NSObject";
}
}