/*******************************************************************************
* Copyright (c) 2007 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/
package org.eclipse.imp.utils;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IContributor;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.imp.language.ILanguageService;
import org.eclipse.imp.language.Language;
import org.eclipse.imp.language.LanguageRegistry;
import org.eclipse.imp.runtime.RuntimePlugin;
import org.osgi.framework.Bundle;
/*
* Licensed Materials - Property of IBM, (c) Copyright IBM Corp. 2005 All Rights
* Reserved
*/
/**
* @author Claffra
* @author rfuhrer@watson.ibm.com
* @author jurgen@vinju.org
*/
public class ExtensionFactory {
/**
* Locate the first extension that matches this language and extension point id,
* and load the class that implements it and return a handle to an object of that class.
*
* @param language
* @param extensionPointID
* @return
* @throws ExtensionException
*/
public static ILanguageService createServiceExtension(Language language,
String extensionPointID) throws ExtensionException {
return createServiceExtensionForPlugin(language, RuntimePlugin.IMP_RUNTIME,
extensionPointID);
}
/**
* Locate all extensions that match the language and extension point id, load their
* classes and collect handles to objects of that class in a set.
* @param language
* @param extensionPointID
* @return
* @throws ExtensionException
*/
public static Set<ILanguageService> createServiceExtensionSet(Language language,
String extensionPointID) throws ExtensionException {
return createServiceExtensionSetForPlugin(language, RuntimePlugin.IMP_RUNTIME,
extensionPointID);
}
/**
* Find a language name in a language descriptor extension.
*
* @param pluginID
* @return
*/
public static String retrieveLanguageIdFromPlugin(String pluginID) {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry()
.getExtensionPoint(RuntimePlugin.IMP_RUNTIME,
RuntimePlugin.LANGUAGE_DESCRIPTOR);
IConfigurationElement[] configElements = extensionPoint
.getConfigurationElements();
for (int i = 0; i < configElements.length; i++) {
IContributor contrib = configElements[i].getContributor();
if (contrib.getName().equals(pluginID)) {
return configElements[i]
.getAttribute(Language.LANGUAGE_ID_ATTR);
}
}
return null;
}
/**
* Locate the first extension that matches this language, plugin, and extension point id,
* then load a class from the element named 'elementName', and return a handle to an
* object of this class.
*
* @param language
* @param pluginID
* @param extensionPointId
* @param elementName
* @return
* @throws ExtensionException
*/
public static ILanguageService createServiceExtensionForPlugin(
Language language, String pluginID, String extensionPointId,
String elementName) throws ExtensionException {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry()
.getExtensionPoint(pluginID, extensionPointId);
if (extensionPoint == null) {
throw new ExtensionException(
"No such language service extension point defined: "
+ pluginID + "." + extensionPointId);
}
ILanguageService service = getLanguageServiceForElement(
extensionPoint, language.getName(), elementName);
if (service == null && languageIsDerived(language)) {
service = createServiceExtensionForPlugin(LanguageRegistry
.findLanguage(language.getDerivedFrom()), pluginID,
extensionPointId, elementName);
}
return service;
}
/**
* Determine whether a language was declared to be derived from another language,
* and whether this other language actually is registered.
* @param language
* @return
*/
private static boolean languageIsDerived(Language language) {
final boolean hasParent = language.getDerivedFrom() != null
&& LanguageRegistry.findLanguage(language.getDerivedFrom()) != null;
return hasParent;
}
/**
* Finds the first extension that matches this language, plugin and extension point id,
* and then loads the class that implements this extension and returns a handle to it.
*
* @param language
* @param pluginID
* @param extensionPointId
* @return
* @throws ExtensionException
*/
public static ILanguageService createServiceExtensionForPlugin(Language language,
String pluginID, String extensionPointId) throws ExtensionException {
return createServiceExtensionForPlugin(language, pluginID,
extensionPointId, "class");
}
/**
* Find all extensions that match this pluginId, language and extension point id,
* then loads the classes that implement these extensions and returns them as
* a set of ILanguageServices.
*
* @param language
* @param pluginID
* @param extensionPointId
* @return
* @throws ExtensionException
*/
public static Set<ILanguageService> createServiceExtensionSetForPlugin(Language language,
String pluginID, String extensionPointId) throws ExtensionException {
IExtensionPoint extensionPoint = Platform.getExtensionRegistry()
.getExtensionPoint(pluginID, extensionPointId);
if (extensionPoint == null) {
throw new ExtensionException(
"No such language service extension point defined: "
+ pluginID + "." + extensionPointId);
}
Set<ILanguageService> services = getLanguageServiceSet(
extensionPoint, language.getName());
if (services.isEmpty() && languageIsDerived(language)) {
final ILanguageService baseServiceImpl = createServiceExtensionForPlugin(
LanguageRegistry.findLanguage(language.getDerivedFrom()),
pluginID, extensionPointId);
if (baseServiceImpl != null) {
services.add(baseServiceImpl);
}
}
return services;
}
/**
* detect whether a certain plugin defines a certain extension for a certain language
* @param pluginID
* @param extensionPointID
* @param language
* @return
*/
@SuppressWarnings("deprecation")
public static boolean languageServiceExists(String pluginID,
String extensionPointID, Language language) {
if (language == null)
return false;
IExtensionPoint extensionPoint = Platform.getExtensionRegistry()
.getExtensionPoint(pluginID, extensionPointID);
IConfigurationElement[] elements = extensionPoint
.getConfigurationElements();
String lowerLang = language.getName().toLowerCase();
if (elements != null) {
for (int n = 0; n < elements.length; n++) {
IConfigurationElement element = elements[n];
Bundle bundle = Platform.getBundle(element
.getDeclaringExtension().getNamespace());
if (bundle != null) {
final String attrValue = element
.getAttribute(Language.LANGUAGE_ID_ATTR);
if (attrValue != null
&& lowerLang.equals(attrValue.toLowerCase())) {
return true;
}
}
}
}
if (languageIsDerived(language)) {
return languageServiceExists(pluginID, extensionPointID,
LanguageRegistry.findLanguage(language.getDerivedFrom()));
}
return false;
}
/**
* Locates all elements that match the language and the particular extension point id,
* and returns a set of loaded ILanguageServices using these elements.
* @param extensionPoint
* @param language
* @return
* @throws ExtensionException
*/
private static Set<ILanguageService> getLanguageServiceSet(
IExtensionPoint extensionPoint, String language)
throws ExtensionException {
IConfigurationElement[] elements = extensionPoint
.getConfigurationElements();
Set<ILanguageService> result = new HashSet<ILanguageService>();
if (elements != null) {
for (int n = 0; n < elements.length; n++) {
IConfigurationElement element = elements[n];
ILanguageService service = loadLanguageService(extensionPoint, language, "class", element);
if (service != null) {
result.add(service);
}
}
}
return result;
}
/**
* Locates the first element that matches the language and the elementName,
* and tries to load a language service from it.
*
* @param extensionPoint
* @param language
* @param elementName
* @return
* @throws ExtensionException
*/
private static ILanguageService getLanguageServiceForElement(
IExtensionPoint extensionPoint, String language, String elementName)
throws ExtensionException {
IConfigurationElement[] elements = extensionPoint
.getConfigurationElements();
if (elements != null) {
for (int n = 0; n < elements.length; n++) {
IConfigurationElement element = elements[n];
ILanguageService service = loadLanguageService(extensionPoint, language, elementName, element);
if (service != null) {
return service;
}
}
}
return null;
}
/**
* Convenience method for actually creating an object from an element in an
* extension point. Catches common exceptions that may be thrown and tries to
* translate them into ServiceExceptions.
*
* @param extensionPoint
* @param language
* @param elementName
* @param element
* @return
* @throws ExtensionException
*/
private static ILanguageService loadLanguageService(IExtensionPoint extensionPoint, String language, String elementName, IConfigurationElement element) throws ExtensionException {
Bundle bundle = Platform.getBundle(element.getDeclaringExtension().getContributor().getName());
// Bundle bundle = Platform.getBundle(element.getDeclaringExtension().getNamespaceIdentifier());
String lowerLang = language.toLowerCase();
if (bundle != null) {
final String attrValue = element.getAttribute(Language.LANGUAGE_ID_ATTR);
if (attrValue != null && lowerLang.equals(attrValue.toLowerCase())) {
try {
return (ILanguageService) element.createExecutableExtension(elementName);
} catch (ClassCastException e) {
logException(new ExtensionException(
"Extension does not point to a class that implements an ILanguageService:"
+ element, e));
} catch (IncompatibleClassChangeError e) {
logException(new ExtensionException("Unable to instantiate implementation of "
+ extensionPoint.getLabel()
+ " plugin for language '"
+ language
+ "' because some class in the plugin is incompatible (out-of-date)", e));
} catch (CoreException e) {
logException(new ExtensionException(
"Unable to instantiate implementation of "
+ extensionPoint.getLabel()
+ " plugin for language '"
+ language
+ "' because of the following low level exception: "
+ e.getStatus().getException(), e));
} catch (NoClassDefFoundError e) {
logException(new ExtensionException(
"Unable to instantiate implementation of "
+ extensionPoint.getLabel()
+ " plugin for language '"
+ language
+ "' because it may not have a public zero argument constructor, or some class referenced by the plugin could not be found in the class path.",
e));
}
}
}
return null;
}
private static void logException(ExtensionException extensionException) {
RuntimePlugin.getInstance().logException(extensionException.getMessage(), extensionException);
}
/**
* Retrieves from an extension an attribute that represents a resource URL
* @param language language that the extension is for
* @param extensionPoint extension point identifier
* @param the label of the attribute that contains the resource URL
*/
@SuppressWarnings("deprecation")
public static URL createResourceURL(String language,
IExtensionPoint extensionPoint, String label) {
IConfigurationElement[] elements = extensionPoint
.getConfigurationElements();
String lowerLabel = label.toLowerCase();
String lowerLang = language.toLowerCase();
if (elements != null) {
for (int n = 0; n < elements.length; n++) {
IConfigurationElement element = elements[n];
Bundle bundle = Platform.getBundle(element
.getDeclaringExtension().getNamespace());
if (bundle != null) {
final String attrValue = element
.getAttribute(Language.LANGUAGE_ID_ATTR);
if (attrValue != null
&& lowerLang.equals(attrValue.toLowerCase())) {
String resourceName = element.getAttribute(lowerLabel);
return bundle.getResource(resourceName);
}
}
}
}
return null;
}
}