package com.aptana.rdt.core.gems;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.InvalidRegistryObjectException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;
import org.rubypeople.rdt.launching.RubyRuntime;
import com.aptana.rdt.AptanaRDTPlugin;
public class ContributedGemRegistry
{
private static Collection<Gem> fContributed;
private ContributedGemRegistry()
{
}
public static Collection<Gem> getContributedGems()
{
if (fContributed == null)
{
Collection<Gem> gems = new ArrayList<Gem>();
IExtensionPoint extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(
AptanaRDTPlugin.PLUGIN_ID, AptanaRDTPlugin.EXTENSION_POINT_GEMS);
IConfigurationElement[] configs = extensionPoint.getConfigurationElements();
for (int i = 0; i < configs.length; i++)
{
IConfigurationElement element = configs[i];
if (!"gem".equals(element.getName())) //$NON-NLS-1$
continue;
try
{
Gem gem = createGem(element);
if (gem == null)
continue;
gems.add(gem);
}
catch (InvalidRegistryObjectException e)
{
AptanaRDTPlugin.log(e);
}
}
fContributed = gems;
}
return fContributed;
}
private static Gem createGem(IConfigurationElement element)
{
boolean install = false;
String autoInstall = element.getAttribute("auto-install"); //$NON-NLS-1$
if (autoInstall != null && autoInstall.trim().length() > 0)
{
install = Boolean.parseBoolean(autoInstall);
}
if (!install)
return null;
String name = element.getAttribute("name"); //$NON-NLS-1$
Gem gem = null;
String path = element.getAttribute("path"); //$NON-NLS-1$
if (path == null || path.trim().length() == 0)
{
gem = new Gem(name, getVersion(element), null);
}
else
{
gem = createLocalGem(name, path, element);
}
String compiles = element.getAttribute("compiles");
gem.setCompiles(Boolean.parseBoolean(compiles));
boolean forceUpdate = false;
String forceUpdateRaw = element.getAttribute("force-update"); //$NON-NLS-1$
if (forceUpdateRaw != null && forceUpdateRaw.trim().length() > 0)
{
forceUpdate = Boolean.parseBoolean(forceUpdateRaw);
}
gem.setForceUpdate(forceUpdate);
return gem;
}
private static Gem createLocalGem(String name, String path, IConfigurationElement element)
{
Bundle bundle = Platform.getBundle(element.getContributor().getName());
URL url = FileLocator.find(bundle, new Path(path), null);
if (url == null)
{
AptanaRDTPlugin.log("Unable to generate a URL for the gem with path: " + path + " in the "
+ element.getContributor().getName() + " plugin");
return null;
}
Gem gem = null;
if (name == null || name.trim().length() == 0)
gem = LocalFileGem.create(url);
else
{
String platform = element.getAttribute("platform"); //$NON-NLS-1$
if (platform == null || platform.trim().length() == 0)
platform = Gem.RUBY_PLATFORM;
gem = new LocalFileGem(url, name, getVersion(element), "", platform);
}
IConfigurationElement[] dependencies = element.getChildren("dependency"); //$NON-NLS-1$
for (int j = 0; j < dependencies.length; j++)
{
String dependency = dependencies[j].getAttribute("name"); //$NON-NLS-1$
((LocalFileGem) gem).addDependency(dependency);
}
return gem;
}
private static String getVersion(IConfigurationElement element)
{
String version = element.getAttribute("version"); //$NON-NLS-1$
if (version != null)
return version;
return Gem.ANY_VERSION;
}
public static Gem getGem(String name)
{
Collection<Gem> gems = filterByPlatform(getContributedGems());
for (Gem gem : gems)
{
if (gem.getName().equals(name))
{ // TODO Find latest version of gem
return gem;
}
}
return null;
}
public static Collection<Gem> filterByPlatform(Collection<Gem> gems)
{
Map<String, Gem> map = new HashMap<String, Gem>();
for (Gem gem : gems)
{
if (map.containsKey(gem.getName()))
{
// If we're using JRuby and this is the JRuby specific gem, use
// it
if (RubyRuntime.currentVMIsJRuby() && gem.getPlatform().equals(Gem.JRUBY_PLATFORM))
{
map.put(gem.getName(), gem);
}
// if we're on windows, not using JRuby, and this is the windows
// specific gem, use it
if (!RubyRuntime.currentVMIsJRuby() && Platform.getOS().equals(Platform.OS_WIN32)
&& gem.getPlatform().equals(Gem.MSWIN32_PLATFORM))
{
// Don't use mswin32 if we're running a cygwin VM
if (RubyRuntime.currentVMIsCygwin())
continue;
map.put(gem.getName(), gem);
}
}
else
{
// Dont't install a windows gem on a non windows platform or on
// cygwin interpreter
if (gem.getPlatform().equals(Gem.MSWIN32_PLATFORM))
{
if (!Platform.getOS().equals(Platform.OS_WIN32))
continue;
if (RubyRuntime.currentVMIsJRuby())
continue;
if (RubyRuntime.currentVMIsCygwin())
continue;
}
// Don't install jruby gems unless we're running a JRuby VM
if (!RubyRuntime.currentVMIsJRuby() && gem.getPlatform().equals(Gem.JRUBY_PLATFORM))
continue;
// Don't install non java gems that have to compile native code
if (gem instanceof LocalFileGem)
{
LocalFileGem localGem = (LocalFileGem) gem;
if (RubyRuntime.currentVMIsJRuby() && !gem.getPlatform().equals(Gem.JRUBY_PLATFORM)
&& localGem.compiles())
continue;
}
map.put(gem.getName(), gem);
}
}
return map.values();
}
public static List<Gem> sortByDependency(Collection<Gem> gems)
{
gems = new ArrayList<Gem>(gems); // make copy of list in case it somehow gets modified while we're using it. ROR-1125
List<Gem> sorted = new ArrayList<Gem>();
while (sorted.size() < gems.size())
{
for (Gem gem : gems)
{
if (sorted.size() == gems.size())
return sorted; // shortcut, when we're done
if (sorted.contains(gem))
continue; // already in sorted list, skip it
boolean add = true;
if (gem instanceof LocalFileGem)
{
LocalFileGem local = (LocalFileGem) gem;
Set<String> dependencies = local.getDependencies();
for (String dependency : dependencies)
{
if (getGemManager().gemInstalled(dependency))
continue; // already installed in system, so we're ok
if (!contains(sorted, dependency))
{ // if it's not ahead of us in list to be installed, skip us for now
add = false;
if (!contains(gems, dependency))
{ // it's not installed, ahead of us, or available - we're screwed, remove this gem from list to be installed
AptanaRDTPlugin.log("Unable to install " + local.toString()
+ " because we were unable to satisfy it's dependencies!");
gems.remove(gem);
}
break;
}
}
}
if (add)
sorted.add(gem);
}
}
return sorted;
}
private static boolean contains(Collection<Gem> gems, String name)
{
for (Gem gem : gems)
{
if (gem.getName().equals(name))
return true;
}
return false;
}
private static IGemManager getGemManager()
{
return AptanaRDTPlugin.getDefault().getGemManager();
}
}