blob: 3e078b32f7beaad57a6f045e6a69ac11a60fe7d4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 xored software, Inc.
*
* 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:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.javascript.typeinfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.dltk.annotations.ConfigurationElement;
import org.eclipse.dltk.javascript.core.JavaScriptPlugin;
import org.eclipse.dltk.javascript.typeinfo.model.NamedElement;
import org.eclipse.dltk.javascript.typeinfo.model.TypeInfoModelResourceSet;
import org.eclipse.dltk.javascript.typeinfo.model.TypeVariable;
import org.eclipse.dltk.utils.LazyExtensionManager;
import org.eclipse.dltk.utils.LazyExtensionManager.Descriptor;
import org.eclipse.dltk.utils.SimpleExtensionManager;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl;
public class TypeInfoManager {
private static final String EXT_POINT = JavaScriptPlugin.PLUGIN_ID
+ ".typeinfo";
private static final String MODEL_ELEMENT = "model";
private static final String RESOURCE_ATTR = "resource";
private static final String URI_ATTR = "uri";
private static final String PROVIDER_ELEMENT = "provider";
private static String trim(String str) {
if (str != null) {
str = str.trim();
if (str.length() == 0) {
str = null;
}
}
return str;
}
private static URI createURI(IConfigurationElement element, String resource) {
return URI.createPlatformPluginURI("/"
+ element.getContributor().getName() + "/" + resource, true);
}
private static IConfigurationElement[] getConfigurationElements() {
return Platform.getExtensionRegistry().getConfigurationElementsFor(
EXT_POINT);
}
private static class ExtManager<E> extends SimpleExtensionManager<E> {
private final String elementName;
ExtManager(Class<E> elementType) {
super(elementType, EXT_POINT);
this.elementName = elementType.getAnnotation(
ConfigurationElement.class).value();
}
@Override
protected E createInstance(IConfigurationElement element) {
if (elementName.equals(element.getName())) {
return super.createInstance(element);
} else {
return null;
}
}
}
public static <E> SimpleExtensionManager<E> createManager(
Class<E> elementType) {
return new ExtManager<E>(elementType);
}
private static final SimpleExtensionManager<IModelBuilder> modelBuilderManager = createManager(IModelBuilder.class);
private static final LazyExtensionManager<ITypeProvider> providerManager = new LazyExtensionManager<ITypeProvider>(
EXT_POINT) {
class TypeProviderDescriptor extends Descriptor<ITypeProvider> {
public TypeProviderDescriptor(
LazyExtensionManager<ITypeProvider> manager,
IConfigurationElement configurationElement) {
super(manager, configurationElement);
}
public ITypeProvider get() {
return create();
}
}
protected boolean isValidElement(IConfigurationElement element) {
return PROVIDER_ELEMENT.equals(element.getName());
}
protected LazyExtensionManager.Descriptor<ITypeProvider> createDescriptor(
IConfigurationElement element) {
return new TypeProviderDescriptor(this, element);
}
};
private static final SimpleExtensionManager<IElementResolver> resolverManager = createManager(IElementResolver.class);
private static final SimpleExtensionManager<IElementConverter> converterManager = createManager(IElementConverter.class);
private static final SimpleExtensionManager<IMemberEvaluator> evaluatorManager = createManager(IMemberEvaluator.class);
private static final SimpleExtensionManager<ITypeInferenceHandlerFactory> nodeHandlerManager = createManager(ITypeInferenceHandlerFactory.class);
private static final SimpleExtensionManager<IRTypeFactory> typeFactoryManager = createManager(IRTypeFactory.class);
static class ModelBuilderRec {
IModelBuilder builder;
int priority;
}
/**
* Return contributed {@link IModelBuilder}s matching to the specified
* context. If context is <code>null</code> all model builders are returned.
*
* @param context
* @return
*/
public static IModelBuilder[] getModelBuilders(ITypeInfoContext context) {
final IModelBuilder[] all = modelBuilderManager.getInstances();
if (context == null) {
return all;
}
final Map<String, ModelBuilderRec> recs = new HashMap<String, ModelBuilderRec>();
for (IModelBuilder builder : all) {
final int priority = builder.priorityFor(context);
if (priority == IModelBuilder.PRIORITY_UNSUPPORTED) {
continue;
}
String featureId = builder.getFeatureId();
ModelBuilderRec rec = recs.get(featureId);
if (rec != null) {
if (priority > rec.priority) {
rec.priority = priority;
rec.builder = builder;
}
} else {
rec = new ModelBuilderRec();
rec.builder = builder;
rec.priority = priority;
recs.put(featureId, rec);
}
}
final IModelBuilder[] result = new IModelBuilder[recs.size()];
int index = 0;
for (ModelBuilderRec rec : recs.values()) {
result[index++] = rec.builder;
}
return result;
}
public static ITypeProvider[] createTypeProviders(ITypeInfoContext context) {
final Descriptor<ITypeProvider>[] descriptors = providerManager
.getDescriptors();
final ITypeProvider[] providers = new ITypeProvider[descriptors.length];
int index = 0;
for (Descriptor<ITypeProvider> descriptor : descriptors) {
final ITypeProvider provider = descriptor.get();
if (provider != null && provider.initialize(context)) {
providers[index++] = provider;
}
}
if (index != providers.length) {
final ITypeProvider[] result = new ITypeProvider[index];
System.arraycopy(providers, 0, result, 0, index);
return result;
}
return providers;
}
public static IElementResolver[] getElementResolvers() {
return resolverManager.getInstances();
}
public static IElementConverter[] getElementConverters() {
return converterManager.getInstances();
}
public static IMemberEvaluator[] getMemberEvaluators() {
return evaluatorManager.getInstances();
}
public static ITypeInferenceHandlerFactory[] getNodeHandlerFactories() {
return nodeHandlerManager.getInstances();
}
public static IRTypeFactory[] getRTypeFactories() {
return typeFactoryManager.getInstances();
}
public static TypeInfoModelResourceSet loadModelResources() {
final TypeInfoModelResourceSet resourceSet = new TypeInfoModelResourceSet();
for (IConfigurationElement element : getConfigurationElements()) {
if (MODEL_ELEMENT.equals(element.getName())) {
final String resource = trim(element
.getAttribute(RESOURCE_ATTR));
final String uri = trim(element.getAttribute(URI_ATTR));
try {
if (uri != null) {
if (resource != null) {
resourceSet
.getURIConverter()
.getURIMap()
.put(URI.createURI(uri),
createURI(element, resource));
resourceSet.getResources().add(
newResource(URI.createURI(uri)));
}
} else if (resource != null) {
resourceSet.getResources().add(
newResource(createURI(element, resource)));
}
} catch (IllegalArgumentException e) {
JavaScriptPlugin.error(e);
}
}
}
/*
* iterate over copy, as it's possible that additional resources would
* appear while loading
*/
for (Resource r : new ArrayList<Resource>(resourceSet.getResources())) {
if (!r.isLoaded()) {
try {
r.load(null);
} catch (IOException e) {
JavaScriptPlugin.error("Error loading " + r.getURI(), e);
if (!r.isLoaded()) {
r.getContents().clear();
}
}
}
}
return resourceSet;
}
public static XMIResource newResource() {
return new TypeInfoXMIResource();
}
public static XMIResource newResource(URI uri) {
return new TypeInfoXMIResource(uri);
}
/**
* Resource implementation which provides human readable fragments for
* {@link TypeVariable} references.
*/
public static class TypeInfoXMIResource extends XMIResourceImpl {
public TypeInfoXMIResource() {
super();
}
public TypeInfoXMIResource(URI uri) {
super(uri);
}
@Override
public EObject getEObject(String uriFragment) {
if (uriFragment.startsWith(ROOT)
&& uriFragment.length() > ROOT.length()
&& uriFragment.charAt(ROOT.length()) == '/') {
final StringTokenizer tokenizer = new StringTokenizer(
uriFragment.substring(ROOT.length() + 1), "/");
Object obj = this;
TOKENS: while (tokenizer.hasMoreTokens()) {
final String name = URI.decode(tokenizer.nextToken());
List<EObject> children = obj instanceof Resource ? ((Resource) obj)
.getContents() : ((EObject) obj).eContents();
for (EObject child : children) {
if (child instanceof NamedElement) {
if (name.equals(((NamedElement) child).getName())) {
obj = child;
continue TOKENS;
}
}
}
return super.getEObject(uriFragment);
}
return (EObject) obj;
}
return super.getEObject(uriFragment);
}
private static final String ROOT = "@ROOT";
@Override
public String getURIFragment(final EObject eObject) {
if (eObject instanceof TypeVariable) {
final List<String> path = new ArrayList<String>();
EObject obj = eObject;
for (;;) {
path.add(((NamedElement) obj).getName());
obj = obj.eContainer();
if (obj == null) {
break;
}
if (!(obj instanceof NamedElement)) {
return super.getURIFragment(eObject);
}
}
final StringBuilder sb = new StringBuilder();
sb.append(ROOT);
for (int i = path.size() - 1; i >= 0; --i) {
sb.append('/');
sb.append(URI.encodeSegment(path.get(i), false));
}
return sb.toString();
}
return super.getURIFragment(eObject);
}
}
}