blob: f5619cca2c5f0ad4a20d568e594f371d096a69e6 [file] [log] [blame]
/**
* Copyright (c) 2011-2012 Eclipse contributors and others.
* 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
*/
package org.eclipse.emf.ecore.xcore.scoping;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Collections.singletonList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelFactory;
import org.eclipse.emf.codegen.ecore.genmodel.GenModelPackage;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xcore.XAnnotationDirective;
import org.eclipse.emf.ecore.xcore.XClass;
import org.eclipse.emf.ecore.xcore.XImportDirective;
import org.eclipse.emf.ecore.xcore.XOperation;
import org.eclipse.emf.ecore.xcore.XPackage;
import org.eclipse.emf.ecore.xcore.XcorePackage;
import org.eclipse.emf.ecore.xcore.mappings.XClassMapping;
import org.eclipse.emf.ecore.xcore.mappings.XOperationMapping;
import org.eclipse.emf.ecore.xcore.mappings.XcoreMapper;
import org.eclipse.emf.ecore.xcore.scoping.types.XcoreJvmTypeScopeProvider;
import org.eclipse.xtext.common.types.JvmGenericType;
import org.eclipse.xtext.common.types.JvmOperation;
import org.eclipse.xtext.common.types.JvmTypeParameter;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.AbstractEObjectDescription;
import org.eclipse.xtext.resource.EObjectDescription;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.Scopes;
import org.eclipse.xtext.scoping.impl.AbstractScope;
import org.eclipse.xtext.scoping.impl.ImportNormalizer;
import org.eclipse.xtext.scoping.impl.ImportScope;
import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider;
import org.eclipse.xtext.scoping.impl.ScopeBasedSelectable;
import org.eclipse.xtext.scoping.impl.SimpleScope;
import org.eclipse.xtext.util.IResourceScopeCache;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.lib.Pair;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class XcoreImportedNamespaceAwareScopeProvider extends ImportedNamespaceAwareLocalScopeProvider
{
@Inject
private IQualifiedNameConverter nameConverter;
@Inject
private IQualifiedNameProvider nameProvider;
@Inject
private IResourceScopeCache cache;
@Inject
private XcoreJvmTypeScopeProvider typeScopeProvider;
@Inject
protected XcoreMapper mapper;
@Override
public IScope getScope(final EObject context, EReference reference)
{
if (reference == XcorePackage.Literals.XIMPORT_DIRECTIVE__IMPORTED_OBJECT)
{
final IScope jvmTypeScope = typeScopeProvider.getScope(context, reference);
final IScope annotationScope = getAnnotationScope(context);
final IScope classifierScope = getClassifierScope(context);
return new ImportableObjectsScope(classifierScope, annotationScope, jvmTypeScope);
}
else if (reference == XcorePackage.Literals.XGENERIC_TYPE__TYPE)
{
return getClassifierScope(context);
}
else if (reference == XcorePackage.Literals.XANNOTATION__SOURCE)
{
return getAnnotationScope(context);
}
else if (TypesPackage.Literals.JVM_TYPE.isSuperTypeOf(reference.getEReferenceType()))
{
return getJvmTypeScope(context, reference);
}
else
{
throw new IllegalArgumentException("Scope for unknown reference " + reference + " requested");
}
}
private IScope getJvmTypeScope(EObject context, EReference reference)
{
EObject eContainer = context.eContainer();
if (eContainer instanceof XOperation)
{
XOperation xOperation = (XOperation)eContainer;
XOperationMapping mapping = mapper.getMapping(xOperation);
JvmOperation jvmOperation = mapping.getJvmOperation();
EList<JvmTypeParameter> typeParameters = jvmOperation.getTypeParameters();
return getJvmTypeScope(context, reference, typeParameters);
}
else if (eContainer instanceof XClass)
{
XClass xClass = (XClass)eContainer;
XClassMapping mapping = mapper.getMapping(xClass);
JvmGenericType jvmGenericType = mapping.getInterfaceType();
EList<JvmTypeParameter> typeParameters = jvmGenericType.getTypeParameters();
return getJvmTypeScope(context, reference, typeParameters);
}
else if (eContainer == null)
{
return typeScopeProvider.getScope(context, reference);
}
else
{
return getJvmTypeScope(eContainer, reference);
}
}
private IScope getJvmTypeScope(final EObject context, final EReference reference, final EList<JvmTypeParameter> typeParameters)
{
final IScope parentScope = getJvmTypeScope(context.eContainer(), reference);
if (typeParameters.isEmpty())
{
return parentScope;
}
else
{
final List<IEObjectDescription> descriptions = new ArrayList<IEObjectDescription>();
for (JvmTypeParameter jvmTypeParameter : typeParameters)
{
descriptions.add(EObjectDescription.create(jvmTypeParameter.getName(), jvmTypeParameter));
}
return cache.get(new Pair<String, EObject>("typeParameters", context), context.eResource(), new Provider<IScope>()
{
public IScope get()
{
IScope typeParameterScope =
new AbstractScope(parentScope, isIgnoreCase(reference))
{
@Override
protected Iterable<IEObjectDescription> getAllLocalElements()
{
return descriptions;
}
};
return new CachingScope(typeParameterScope);
}
});
}
}
private IScope getAnnotationScope(final EObject context)
{
final boolean isImport = context instanceof XImportDirective;
String cacheKey = isImport ? "import.annotation.scope" : "annotation.scope";
final Resource resource = context.eResource();
return cache.get(cacheKey, resource, new Provider<IScope>()
{
public IScope get()
{
IScope globalScope = getGlobalScope(resource, XcorePackage.Literals.XANNOTATION__SOURCE);
ImportNormalizer xcoreLang = new ImportNormalizer(nameConverter.toQualifiedName("xcore.lang"), true, false);
ImportScope xcoreLangScope = createImportScope(globalScope, singletonList(xcoreLang), null, XcorePackage.Literals.XANNOTATION_DIRECTIVE, false);
List<ImportNormalizer> importedNamespaceResolvers = isImport ? Collections.<ImportNormalizer>emptyList() : getImportedNamespaceResolvers(EcoreUtil.getRootContainer(context), false);
ImportScope importScope = createImportScope(xcoreLangScope, importedNamespaceResolvers, null, XcorePackage.Literals.XANNOTATION_DIRECTIVE, false);
IScope resourceScope = getLocalAnnotationsScope(resource, importScope);
return new CachingScope(resourceScope);
}
});
}
private IScope getClassifierScope(final EObject context)
{
final boolean isImport = context instanceof XImportDirective;
String cacheKey = isImport ? "import.classifier.scope" : "classifier.scope";
final Resource resource = context.eResource();
return cache.get(cacheKey, resource, new Provider<IScope>()
{
public IScope get()
{
IScope globalScope = getGlobalScope(resource, XcorePackage.Literals.XGENERIC_TYPE__TYPE, new Predicate<IEObjectDescription>()
{
public boolean apply(IEObjectDescription input)
{
EClass eClass = input.getEClass();
return GenModelPackage.Literals.GEN_CLASSIFIER.isSuperTypeOf(eClass) || eClass == GenModelPackage.Literals.GEN_ANNOTATION || eClass == GenModelPackage.Literals.GEN_TYPE_PARAMETER;
}
});
EcoreDataTypeAliasingScope aliasingScope = new EcoreDataTypeAliasingScope(globalScope, nameConverter);
List<ImportNormalizer> importedNamespaceResolvers = Collections.emptyList();
if (!isImport)
{
importedNamespaceResolvers = Lists.newArrayList(getImportedNamespaceResolvers(EcoreUtil.getRootContainer(context), false));
Set<String> names = new HashSet<String>();
for (ImportNormalizer importNormalizer : importedNamespaceResolvers)
{
if (!importNormalizer.hasWildCard())
{
names.add(importNormalizer.getImportedNamespacePrefix().getLastSegment());
}
}
for (String implicitImport : IMPLICIT_IMPORTS)
{
ImportNormalizer importedNamespaceResolver = createImportedNamespaceResolver(implicitImport, false);
if (!names.contains(importedNamespaceResolver.getImportedNamespacePrefix().getLastSegment()))
{
importedNamespaceResolvers.add(importedNamespaceResolver);
}
}
}
ImportScope importScope = createImportScope(aliasingScope, importedNamespaceResolvers, null, GenModelPackage.Literals.GEN_BASE, false);
IScope resourceScope = getLocalClassifiersScope(resource, importScope);
return new CachingScope(resourceScope);
}
});
}
/*
* This is much faster than creating resource descriptions for all elements and then filtering out the interesting ones.
*/
private IScope getLocalAnnotationsScope(Resource resource, IScope parent)
{
XPackage xPackage = (XPackage)resource.getContents().get(0);
List<XAnnotationDirective> directives = xPackage.getAnnotationDirectives();
return directives.isEmpty() ? parent : createLocalElementsScope(parent, directives, xPackage, GenModelPackage.Literals.GEN_BASE);
}
/*
* This is much faster than creating resource descriptions for all elements and then filtering out the interesting ones.
*/
private IScope getLocalClassifiersScope(Resource resource, IScope parent)
{
EList<EObject> contents = resource.getContents();
XPackage xPackage = (XPackage)contents.get(0);
List<GenClassifier> classifiers = newArrayList();
EList<GenPackage> genPackages = ((GenModel)contents.get(1)).getGenPackages();
for (GenPackage genPackage : genPackages)
{
classifiers.addAll(genPackage.getGenClassifiers());
}
return classifiers.isEmpty() ? parent : createLocalElementsScope(parent, classifiers, xPackage, GenModelPackage.Literals.GEN_BASE);
}
private IScope createLocalElementsScope(IScope parent, List<? extends EObject> elements, XPackage xPackage, EClass type)
{
List<IEObjectDescription> eagerlyResolvedDescriptions = newArrayList(Scopes.scopedElementsFor(elements, nameProvider));
IScope localScope = new SimpleScope(eagerlyResolvedDescriptions);
String name = xPackage.getName();
if (Strings.isEmpty(name))
{
return localScope;
}
ImportNormalizer localNormalizer = doCreateImportNormalizer(nameConverter.toQualifiedName(name), true, false);
return createImportScope(parent, singletonList(localNormalizer), new ScopeBasedSelectable(localScope), type, false);
}
/*
* This creates the same result as the super implementation,
* but much faster since it knows where to look for imports instead of iterating all contents
*/
@Override
protected List<ImportNormalizer> internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase)
{
if (context instanceof XPackage)
{
List<ImportNormalizer> importedNamespaceResolvers = Lists.newArrayList();
XPackage xPackage = (XPackage)context;
for (XImportDirective xImportDirective : xPackage.getImportDirectives())
{
ImportNormalizer normalizer = createImportedNamespaceResolver(xImportDirective.getImportedNamespace(), ignoreCase);
if (normalizer != null)
{
importedNamespaceResolvers.add(normalizer);
}
}
return importedNamespaceResolvers;
}
return Collections.emptyList();
}
public static final EDataType[] IMPLICIT_ALIASES =
{
EcorePackage.Literals.EBIG_DECIMAL,
EcorePackage.Literals.EBIG_INTEGER,
EcorePackage.Literals.EBOOLEAN,
EcorePackage.Literals.EBOOLEAN_OBJECT,
EcorePackage.Literals.EBYTE,
EcorePackage.Literals.EBYTE_OBJECT,
EcorePackage.Literals.ECHAR,
EcorePackage.Literals.ECHARACTER_OBJECT,
EcorePackage.Literals.EDATE,
EcorePackage.Literals.EDOUBLE,
EcorePackage.Literals.EDOUBLE_OBJECT,
EcorePackage.Literals.EFLOAT,
EcorePackage.Literals.EFLOAT_OBJECT,
EcorePackage.Literals.EINT,
EcorePackage.Literals.EINTEGER_OBJECT,
EcorePackage.Literals.EJAVA_CLASS,
EcorePackage.Literals.EJAVA_OBJECT,
EcorePackage.Literals.ELONG,
EcorePackage.Literals.ELONG_OBJECT,
EcorePackage.Literals.ESHORT,
EcorePackage.Literals.ESHORT_OBJECT,
EcorePackage.Literals.ESTRING
};
private static final String[] IMPLICIT_IMPORTS;
static
{
List<String> implicitImports = Lists.newArrayList();
for (EDataType eDataType : IMPLICIT_ALIASES)
{
String instanceClassName = eDataType.getInstanceClassName();
if (instanceClassName.indexOf('.') != -1 && !instanceClassName.startsWith("java.lang."))
{
implicitImports.add(instanceClassName);
}
}
implicitImports.add("java.lang.*");
IMPLICIT_IMPORTS = implicitImports.toArray(new String[implicitImports.size()]);
}
private static final class ImportableObjectsScope implements IScope
{
private final IScope classifierScope;
private final IScope jvmTypeScope;
private final IScope annotationScope;
private ImportableObjectsScope(IScope classifierScope, IScope annotationScope, IScope jvmTypeScope)
{
this.classifierScope = classifierScope;
this.jvmTypeScope = jvmTypeScope;
this.annotationScope = annotationScope;
}
public IEObjectDescription getSingleElement(QualifiedName name)
{
IEObjectDescription result = classifierScope.getSingleElement(name);
if (result == null || result instanceof EcoreDataTypeAliasingScope.EcoreDataTypeAliasEObjectDescription)
{
result = annotationScope.getSingleElement(name);
}
if (result == null)
{
result = jvmTypeScope.getSingleElement(name);
}
return result;
}
public Iterable<IEObjectDescription> getElements(QualifiedName name)
{
return Iterables.concat(classifierScope.getElements(name), annotationScope.getElements(name), jvmTypeScope.getElements(name));
}
public IEObjectDescription getSingleElement(EObject object)
{
IEObjectDescription result = classifierScope.getSingleElement(object);
if (result == null)
{
result = annotationScope.getSingleElement(object);
}
if (result == null)
{
result = jvmTypeScope.getSingleElement(object);
}
return result;
}
public Iterable<IEObjectDescription> getElements(EObject object)
{
return Iterables.concat(classifierScope.getElements(object), annotationScope.getElements(object), jvmTypeScope.getElements(object));
}
public Iterable<IEObjectDescription> getAllElements()
{
return Iterables.concat(classifierScope.getAllElements(), annotationScope.getAllElements(), jvmTypeScope.getAllElements());
}
}
private static class EcoreDataTypeAliasingScope extends AbstractScope
{
private class EcoreDataTypeAliasEObjectDescription extends AbstractEObjectDescription
{
private final QualifiedName qualifiedName;
private final QualifiedName actualQualifiedName;
private final EDataType eDataType;
private EObject eObject;
private EcoreDataTypeAliasEObjectDescription(QualifiedName qualifiedName, QualifiedName actualQualifiedName, EDataType eDataType)
{
this.qualifiedName = qualifiedName;
this.actualQualifiedName = actualQualifiedName;
this.eDataType = eDataType;
}
public QualifiedName getQualifiedName()
{
return qualifiedName;
}
public QualifiedName getName()
{
return qualifiedName;
}
public URI getEObjectURI()
{
IEObjectDescription element = getElement();
return element == null ? getSyntheticEObjectURI() : element.getEObjectURI();
}
public EObject getEObjectOrProxy()
{
IEObjectDescription element = getElement();
if (element == null)
{
if (eObject == null)
{
InternalEObject genDataType = (InternalEObject)GenModelFactory.eINSTANCE.createGenDataType();
genDataType.eSetProxyURI(getSyntheticEObjectURI());
eObject = genDataType;
}
return eObject;
}
else
{
return element.getEObjectOrProxy();
}
}
public EClass getEClass()
{
return GenModelPackage.Literals.GEN_DATA_TYPE;
}
protected URI getSyntheticEObjectURI()
{
return ECORE_GEN_MODEL_URI.appendFragment("//ecore/" + eDataType.getName());
}
protected IEObjectDescription getElement()
{
IEObjectDescription element = getParent().getSingleElement(actualQualifiedName);
return element;
}
}
private static final URI ECORE_GEN_MODEL_URI = URI.createURI("platform:/resource/org.eclipse.emf.ecore/model/Ecore.genmodel");
private IQualifiedNameConverter nameConverter;
public EcoreDataTypeAliasingScope(IScope parent, IQualifiedNameConverter nameConverter)
{
super(parent, false);
this.nameConverter = nameConverter;
}
@Override
protected Iterable<IEObjectDescription> getAllLocalElements()
{
ArrayList<IEObjectDescription> result = new ArrayList<IEObjectDescription>();
for (final EDataType eDataType : IMPLICIT_ALIASES)
{
String instanceClassName = eDataType.getInstanceClassName();
final QualifiedName actualQualifiedName = QualifiedName.create("org", "eclipse", "emf", "ecore", eDataType.getName());
final QualifiedName qualifiedName = nameConverter.toQualifiedName(instanceClassName);
AbstractEObjectDescription qualifiedObjectDescription = new EcoreDataTypeAliasEObjectDescription(qualifiedName, actualQualifiedName, eDataType);
result.add(qualifiedObjectDescription);
}
return result;
}
}
private static final URI LOGICAL_XCORE_LANG_URI = URI.createURI("platform:/resource/org.eclipse.emf.ecore.xcore.lib/model/XcoreLang.xcore");
private static final URI PHYSICAL_XCORE_LANG_URI = URI.createURI("platform:/plugin/org.eclipse.emf.ecore.xcore.lib/model/XcoreLang.xcore");
public static Resource getXcoreLangResource(ResourceSet resourceSet)
{
Resource xcoreLangResource = resourceSet.getResource(LOGICAL_XCORE_LANG_URI, false);
if (xcoreLangResource == null)
{
xcoreLangResource = resourceSet.getResource(PHYSICAL_XCORE_LANG_URI, true);
xcoreLangResource.setURI(LOGICAL_XCORE_LANG_URI);
}
return xcoreLangResource;
}
private static class CachingScope extends AbstractScope
{
private final Map<QualifiedName, IEObjectDescription> cache;
public CachingScope(IScope parent)
{
super(parent, true);
this.cache = Maps.newHashMapWithExpectedSize(50);
}
@Override
public IEObjectDescription getSingleElement(QualifiedName name)
{
IEObjectDescription cached = cache.get(name);
if (cached == null)
{
if (cache.containsKey(name))
{
return null;
}
cached = getParent().getSingleElement(name);
cache.put(name, cached);
}
return cached;
}
@Override
public Iterable<IEObjectDescription> getElements(QualifiedName name)
{
IEObjectDescription element = getSingleElement(name);
return element == null ? Collections.<IEObjectDescription>emptyList() : Collections.singletonList(element);
}
@Override
protected Iterable<IEObjectDescription> getAllLocalElements()
{
return Collections.emptyList();
}
}
}