package org.radrails.rails.internal.core;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.jruby.ast.FCallNode;
import org.jruby.ast.Node;
import org.jruby.ast.SymbolNode;
import org.radrails.rails.core.Inflector;
import org.radrails.rails.core.RailsLog;
import org.rubypeople.rdt.core.IMethod;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IType;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.codeassist.CodeResolver;
import org.rubypeople.rdt.core.codeassist.ResolveContext;
import org.rubypeople.rdt.core.search.CollectingSearchRequestor;
import org.rubypeople.rdt.core.search.IRubySearchConstants;
import org.rubypeople.rdt.core.search.IRubySearchScope;
import org.rubypeople.rdt.core.search.SearchEngine;
import org.rubypeople.rdt.core.search.SearchMatch;
import org.rubypeople.rdt.core.search.SearchParticipant;
import org.rubypeople.rdt.core.search.SearchPattern;
import org.rubypeople.rdt.core.util.Util;
import org.rubypeople.rdt.internal.ti.util.FirstPrecursorNodeLocator;
import org.rubypeople.rdt.internal.ti.util.INodeAcceptor;
public class RailsCodeResolver extends CodeResolver {
private static final String HAS_AND_BELONGS_TO_MANY = "has_and_belongs_to_many";
private static final String HAS_ONE = "has_one";
private static final String HAS_MANY = "has_many";
private static final String BELONGS_TO = "belongs_to";
@Override
public void select(ResolveContext context) throws RubyModelException {
if (tryResolvingAssociation(context)) return;
narrowResolvedForMigrations(context);
}
private void narrowResolvedForMigrations(ResolveContext context) throws RubyModelException {
IRubyElement[] resolved = context.getResolved();
if (resolved == null || resolved.length <= 1) return;
IType primary = context.getScript().findPrimaryType();
if (primary == null) return;
String superclass = primary.getSuperclassName();
if (!superclass.equals("ActiveRecord::Migration")) return;
String name = resolved[0].getElementName();
if (!name.equals("create_table")) return;
// filter to ActiveRecord::ConnectionsAdapters::SchemaStatements.create_table
for (int i = 0; i < resolved.length; i++) {
String typeName = ((IMethod) resolved[i]).getDeclaringType().getFullyQualifiedName();
if (typeName.equals("ActiveRecord::ConnectionAdapters::SchemaStatements")) {
context.putResolved(new IRubyElement[] {resolved[i]});
return;
}
}
}
private boolean tryResolvingAssociation(ResolveContext context) throws RubyModelException {
Node selected = context.getSelectedNode();
if (!(selected instanceof SymbolNode)) return false;
SymbolNode sym = (SymbolNode) selected;
String symbolName = sym.getName();
Node methodCall = FirstPrecursorNodeLocator.Instance().findFirstPrecursor(context.getAST(), selected.getPosition().getStartOffset(), new INodeAcceptor() {
public boolean doesAccept(Node node) {
return node instanceof FCallNode;
}
});
if (methodCall == null) return false;
FCallNode fCall = (FCallNode) methodCall;
String methodName = fCall.getName();
String modelName;
if (methodName.equals(HAS_MANY) || methodName.equals(HAS_AND_BELONGS_TO_MANY)) {
modelName = Inflector.singularize(symbolName);
modelName = Util.underscoresToCamelCase(modelName);
} else if (methodName.equals(BELONGS_TO) || methodName.equals(HAS_ONE)) {
modelName = Util.underscoresToCamelCase(symbolName);
} else {
return false;
}
try {
SearchEngine engine = new SearchEngine();
SearchParticipant[] participants = new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() };
CollectingSearchRequestor requestor = new CollectingSearchRequestor();
SearchPattern pattern = SearchPattern.createPattern(IRubyElement.TYPE, modelName, IRubySearchConstants.DECLARATIONS, SearchPattern.R_EXACT_MATCH);
IRubySearchScope scope = SearchEngine.createRubySearchScope(new IRubyElement[] {context.getScript().getRubyProject()});
engine.search(pattern, participants, scope, requestor, null);
List<SearchMatch> matches = requestor.getResults();
for (SearchMatch searchMatch : matches) {
IRubyElement element = (IRubyElement) searchMatch.getElement();
context.putResolved(new IRubyElement[] {element});
return true;
}
} catch (CoreException e) {
RailsLog.log(e);
}
return false;
}
}