package com.aptana.rdt.internal.core.builder;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.jruby.ast.FCallNode;
import org.jruby.ast.Node;
import org.jruby.lexer.yacc.SyntaxException;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.core.compiler.BuildContext;
import org.rubypeople.rdt.core.compiler.CompilationParticipant;
import org.rubypeople.rdt.internal.core.parser.InOrderVisitor;
import org.rubypeople.rdt.internal.core.parser.RubyParser;
import org.rubypeople.rdt.internal.core.util.ASTUtil;
import com.aptana.rdt.AptanaRDTPlugin;
import com.aptana.rdt.core.gems.Gem;
public class GemLoadpathAdder extends CompilationParticipant
{
private IRubyProject project;
@Override
public int aboutToBuild(IRubyProject project)
{
this.project = project;
return super.aboutToBuild(project);
}
@Override
public void buildStarting(BuildContext[] files, boolean isBatch, IProgressMonitor monitor)
{
SubMonitor sub = SubMonitor.convert(monitor, 100);
Collection<String> gems = getReferencedGems(files, sub.newChild(80));
addReferencedGemsToLoadpath(gems, sub.newChild(20));
sub.done();
}
private void addReferencedGemsToLoadpath(Collection<String> gems, IProgressMonitor monitor)
{
SubMonitor sub = SubMonitor.convert(monitor, gems.size());
for (String gemName : gems)
{
if (gemName.equals("rails"))
continue; // HACK don't do this for rails
try
{
AptanaRDTPlugin.addGemLoadPath(project, new Gem(gemName, "", ""), sub.newChild(1));
}
catch (RubyModelException e)
{
AptanaRDTPlugin.log(e);
}
}
sub.done();
}
private Collection<String> getReferencedGems(BuildContext[] files, IProgressMonitor monitor)
{
SubMonitor sub = SubMonitor.convert(monitor, files.length);
RubyParser parser = new RubyParser();
Collection<String> gems = new HashSet<String>();
for (BuildContext context : files)
{
sub.subTask("Looking for referenced gems in " + context.getFile().getLocation().toPortableString());
gems.addAll(getGemNames(parser, context));
sub.worked(1);
}
sub.done();
return gems;
}
private Collection<String> getGemNames(RubyParser parser, BuildContext context)
{
try
{
Node root = context.getAST();
if (root == null)
return Collections.emptyList();
GemVisitor visitor = new GemVisitor();
root.accept(visitor);
return visitor.getGems();
}
catch (SyntaxException e)
{
// ignore
}
catch (Exception e)
{
RubyCore.log(e);
}
return Collections.emptyList();
}
@Override
public boolean isActive(IRubyProject project)
{
return true;
}
private static class GemVisitor extends InOrderVisitor
{
private Set<String> gems;
public GemVisitor()
{
gems = new HashSet<String>();
}
@Override
public Object visitFCallNode(FCallNode iVisited)
{
String name = iVisited.getName();
if (name.equals("gem") || name.equals("require_gem"))
{
List<String> args = ASTUtil.getArgumentsFromFunctionCall(iVisited);
if (args != null && !args.isEmpty())
{
String gemName = args.get(0);
if (gemName.startsWith("\"") || gemName.startsWith("'"))
{
gemName = new String(gemName.substring(1, gemName.length() - 1));
}
gems.add(gemName);
}
}
return super.visitFCallNode(iVisited);
}
public Collection<String> getGems()
{
return gems;
}
}
}