/**
* 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 java.util.Collections.singletonList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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.EMFPlugin;
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.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
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.util.ExtendedMetaData;
import org.eclipse.emf.ecore.xcore.XImportDirective;
import org.eclipse.emf.ecore.xcore.XPackage;
import org.eclipse.emf.ecore.xcore.XcoreFactory;
import org.eclipse.emf.ecore.xcore.XcorePackage;
import org.eclipse.emf.ecore.xcore.XcorePlugin;
import org.eclipse.emf.ecore.xcore.util.EcoreXcoreBuilder;
import org.eclipse.emf.ecore.xcore.util.XcoreJvmInferrer;
import org.eclipse.xtext.common.types.TypesPackage;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.AbstractEObjectDescription;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.ISelectable;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.AbstractScope;
import org.eclipse.xtext.scoping.impl.FilteringScope;
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.util.Strings;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Provider;
public class XcoreImportedNamespaceAwareScopeProvider extends ImportedNamespaceAwareLocalScopeProvider
{
@Inject
private IQualifiedNameConverter nameConverter;
@Inject
private Provider<EcoreXcoreBuilder> ecoreXcoreBuilderProvider;
@Inject
private XcoreJvmInferrer jvmInferrer;
@Inject
private IResourceDescription.Manager manager;
@Override
protected IScope getResourceScope(IScope parent, EObject context, EReference reference)
{
IScope resourceScope = super.getResourceScope(parent, context, reference);
if (reference == XcorePackage.Literals.XGENERIC_TYPE__TYPE)
{
return
new FilteringScope
(new ImportScope
(getImplicitImports(false),
new EClassifierScope(resourceScope, context.eResource().getResourceSet(), nameConverter, ecoreXcoreBuilderProvider, jvmInferrer),
null,
GenModelPackage.Literals.GEN_BASE,
false),
new Predicate<IEObjectDescription>()
{
public boolean apply(IEObjectDescription input)
{
EObject eObjectOrProxy = input.getEObjectOrProxy();
return !(eObjectOrProxy instanceof GenPackage);
}
});
}
else if (reference == XcorePackage.Literals.XANNOTATION__SOURCE)
{
return
new ImportScope
(Collections.singletonList(new ImportNormalizer(nameConverter.toQualifiedName("xcore.lang"), true, false)),
new XAnnotationDirectiveScope(resourceScope, context.eResource().getResourceSet()),
null,
GenModelPackage.Literals.GEN_BASE,
false);
}
else
{
return resourceScope;
}
}
@Override
public IScope getScope(EObject context, EReference reference)
{
if (reference == XcorePackage.Literals.XIMPORT_DIRECTIVE__IMPORTED_OBJECT)
{
final IScope classifierScope = getResourceScope(context.eResource(), XcorePackage.Literals.XGENERIC_TYPE__TYPE);
final IScope annotationScope = getResourceScope(context.eResource(), XcorePackage.Literals.XANNOTATION__SOURCE);
final IScope jvmTypeScope = getResourceScope(context.eResource(), TypesPackage.Literals.JVM_PARAMETERIZED_TYPE_REFERENCE__TYPE);
return
new IScope()
{
public IEObjectDescription getSingleElement(QualifiedName name)
{
IEObjectDescription result = classifierScope.getSingleElement(name);
// Ignore also any incidental matches to aliased Ecore data types.
//
if (result == null || result instanceof EClassifierScope.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());
}
};
}
else
{
return super.getScope(context, reference);
}
}
@Override
protected List<ImportNormalizer> internalGetImportedNamespaceResolvers(EObject context, boolean ignoreCase)
{
if (!(context instanceof XPackage))
{
return Collections.emptyList();
}
else
{
List<ImportNormalizer> importedNamespaceResolvers = Lists.newArrayList();
XPackage xPackage = (XPackage)context;
for (XImportDirective xImportDirective : xPackage.getImportDirectives())
{
importedNamespaceResolvers.add(createImportedNamespaceResolver(xImportDirective.getImportedNamespace(), ignoreCase));
}
String name = xPackage.getName();
if (!Strings.isEmpty(name))
{
importedNamespaceResolvers.add(new ImportNormalizer(nameConverter.toQualifiedName(name), true, ignoreCase));
}
return importedNamespaceResolvers;
}
}
@Override
protected ImportNormalizer createImportedNamespaceResolver(String namespace, boolean ignoreCase)
{
if (!Strings.isEmpty(namespace))
{
QualifiedName importedNamespace = nameConverter.toQualifiedName(namespace);
if (importedNamespace != null && importedNamespace.getSegmentCount() >= 1)
{
if (importedNamespace.getLastSegment().equals(getWildCard()))
{
if (importedNamespace.getSegmentCount() > 1)
{
return createImportNormalizer(importedNamespace.skipLast(1), true, ignoreCase);
}
}
else
{
return createImportNormalizer(importedNamespace, false, ignoreCase);
}
}
}
return null;
}
@Override
protected IScope getLocalElementsScope(IScope parent, EObject context, EReference reference)
{
IScope result = parent;
ISelectable allDescriptions = getAllDescriptions(context.eResource());
ScopeBasedSelectable parentSelectable = new ScopeBasedSelectable(parent);
QualifiedName name = getQualifiedNameOfLocalElement(context);
boolean ignoreCase = isIgnoreCase(reference);
if (context instanceof XPackage)
{
result = createImportScope(result, singletonList(createImportNormalizer(QualifiedName.create("java", "lang"), true, false)), parentSelectable, reference.getEReferenceType(), isIgnoreCase(reference));
}
List<ImportNormalizer> namespaceResolvers = getImportedNamespaceResolvers(context, ignoreCase);
if (!namespaceResolvers.isEmpty())
{
result = createImportScope(result, namespaceResolvers, parentSelectable, reference.getEReferenceType(), ignoreCase);
}
if (name != null)
{
ImportNormalizer localNormalizer = createImportNormalizer(name, true, ignoreCase);
result = createImportScope(result, singletonList(localNormalizer), allDescriptions, reference.getEReferenceType(), ignoreCase);
}
return result;
}
protected ImportNormalizer createImportNormalizer(QualifiedName importedNamespace, boolean wildCard, boolean ignoreCase)
{
return
new ImportNormalizer(importedNamespace, wildCard, ignoreCase)
{
@Override
public QualifiedName deresolve(QualifiedName fullyQualifiedName)
{
QualifiedName importedNamespacePrefix = getImportedNamespacePrefix();
boolean ignoreCase = isIgnoreCase();
if (hasWildCard())
{
if (fullyQualifiedName.getSegmentCount() != importedNamespacePrefix.getSegmentCount() &&
(ignoreCase ? fullyQualifiedName.startsWithIgnoreCase(importedNamespacePrefix) : fullyQualifiedName.startsWith(importedNamespacePrefix)))
{
return fullyQualifiedName.skipFirst(importedNamespacePrefix.getSegmentCount());
}
}
else
{
if (fullyQualifiedName.equals(importedNamespacePrefix))
{
String lastSegment = fullyQualifiedName.getLastSegment();
int dollar = lastSegment.lastIndexOf('$');
return QualifiedName.create(dollar >= 0 ? lastSegment.substring(dollar + 1) : fullyQualifiedName.getLastSegment());
}
QualifiedName fullyQualifiedNameBase = fullyQualifiedName.skipLast(1);
QualifiedName importedNamespacePrefixBase = importedNamespacePrefix.skipLast(1);
if (ignoreCase ? fullyQualifiedNameBase.equalsIgnoreCase(importedNamespacePrefixBase) : fullyQualifiedNameBase.equals(importedNamespacePrefixBase))
{
String lastImportedSegment = importedNamespacePrefix.getLastSegment();
String lastSegment = fullyQualifiedName.getLastSegment();
int lastImportedSegmentLength = lastImportedSegment.length();
if (lastSegment.length() > lastImportedSegmentLength &&
lastSegment.charAt(lastImportedSegmentLength) == '$' &&
(ignoreCase ?
lastSegment.substring(0, lastImportedSegmentLength).equalsIgnoreCase(lastImportedSegment) :
lastSegment.startsWith(lastImportedSegment)))
{
int dollar = lastImportedSegment.lastIndexOf('$');
return
dollar == -1 ?
fullyQualifiedName.skipFirst(importedNamespacePrefix.getSegmentCount() - 1) :
QualifiedName.create(lastSegment.substring(dollar + 1));
}
}
}
return null;
}
@Override
public QualifiedName resolve(QualifiedName relativeName)
{
QualifiedName importedNamespacePrefix = getImportedNamespacePrefix();
if (hasWildCard())
{
return importedNamespacePrefix.append(relativeName);
}
else
{
if (relativeName.getSegmentCount() == 1)
{
String lastSegment = relativeName.getLastSegment();
String lastImportedSegment = importedNamespacePrefix.getLastSegment();
boolean ignoreCase = isIgnoreCase();
if (ignoreCase ? lastSegment.equalsIgnoreCase(lastImportedSegment) : lastSegment.equals(lastImportedSegment))
{
return importedNamespacePrefix;
}
else
{
int dollar = lastSegment.indexOf('$');
if (dollar >= 0)
{
int lastImportedSegmentLength = lastImportedSegment.length();
if (dollar == lastImportedSegmentLength &&
(ignoreCase ?
(lastSegment.length() > lastImportedSegmentLength && lastSegment.substring(0, lastImportedSegmentLength).equalsIgnoreCase(lastImportedSegment)) :
lastSegment.startsWith(lastImportedSegment)))
{
return importedNamespacePrefix.skipLast(1).append(lastSegment);
}
}
int importedDollar = lastImportedSegment.lastIndexOf('$');
if (importedDollar >= 0)
{
String nestedTypeName = lastImportedSegment.substring(importedDollar + 1);
int nestedTypeNameLength = nestedTypeName.length();
if (ignoreCase ?
(lastSegment.length() > nestedTypeNameLength && lastSegment.substring(0, nestedTypeNameLength).equalsIgnoreCase(nestedTypeName)) :
lastSegment.startsWith(nestedTypeName))
{
if (nestedTypeNameLength == lastSegment.length())
{
return importedNamespacePrefix;
}
else if (lastSegment.charAt(nestedTypeNameLength) == '$')
{
return importedNamespacePrefix.skipLast(1).append(lastImportedSegment + lastSegment.substring(nestedTypeNameLength));
}
}
}
}
}
}
return null;
}
};
}
@Override
protected ImportScope createImportScope(IScope parent, List<ImportNormalizer> namespaceResolvers, ISelectable importFrom, final EClass type, boolean ignoreCase)
{
// Ensure that qualified names with more than one component don't resolve against wildcard imports.
//
return
new ImportScope(namespaceResolvers, parent, importFrom, type, ignoreCase)
{
@Override
protected IEObjectDescription getSingleLocalElementByName(QualifiedName name)
{
return name.getSegmentCount() > 1 && type == GenModelPackage.Literals.GEN_BASE ? null : super.getSingleLocalElementByName(name);
}
@Override
protected Iterable<IEObjectDescription> getLocalElementsByName(QualifiedName name)
{
return name.getSegmentCount() > 1 && type == GenModelPackage.Literals.GEN_BASE ? Collections.<IEObjectDescription>emptyList() : super.getLocalElementsByName(name);
}
};
}
@Override
protected boolean isRelativeImport()
{
return false;
}
@Override
protected ISelectable internalGetAllDescriptions(Resource resource)
{
IResourceDescription description = manager.getResourceDescription(resource);
return description;
}
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
};
protected static class EClassifierScope 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_XCORE_URI.appendFragment("/1/ecore/" + eDataType.getName());
}
protected IEObjectDescription getElement()
{
IEObjectDescription element = getParent().getSingleElement(actualQualifiedName);
if (element == null)
{
Resource ecoreXcoreResource = resourceSet.getResource(ECORE_XCORE_URI, false);
if (ecoreXcoreResource == null)
{
Resource genModelResource = resourceSet.getResource(ECORE_GEN_MODEL_URI, true);
GenModel genModel = (GenModel)genModelResource.getContents().get(0);
ecoreXcoreResource = resourceSet.getResource(ECORE_XCORE_URI, false);
EPackage ePackage = genModel.getGenPackages().get(0).getEcorePackage();
Resource ecoreResource = ePackage.eResource();
EcoreXcoreBuilder ecoreXcoreBuilder = ecoreXcoreBuilderProvider.get();
ecoreXcoreBuilder.initialize(genModel);
EcoreUtil.resolveAll(genModel);
XPackage xPackage = ecoreXcoreBuilder.getXPackage(ePackage);
ecoreXcoreResource = resourceSet.createResource(ECORE_XCORE_URI);
ecoreXcoreResource.getContents().add(xPackage);
ecoreXcoreResource.getContents().add(genModel);
ecoreXcoreResource.getContents().add(ePackage);
ecoreXcoreBuilder.link();
ecoreXcoreResource.getContents().addAll(jvmInferrer.inferElements(genModel));
jvmInferrer.inferDeepStructure(genModel);
resourceSet.getResources().remove(genModelResource);
resourceSet.getResources().remove(ecoreResource);
}
eObject = ecoreXcoreResource.getEObject("/1/ecore/" + eDataType.getName());
}
return element;
}
}
private static final URI ECORE_XCORE_URI = URI.createURI("platform:/plugin/org.eclipse.emf.ecore/model/Ecore.xcore");
private static final URI ECORE_GEN_MODEL_URI =
EMFPlugin.IS_ECLIPSE_RUNNING ?
URI.createURI("platform:/plugin/org.eclipse.emf.ecore/model/Ecore.genmodel") :
URI.createURI(EcorePlugin.INSTANCE.getBaseURL().toString() + "model/Ecore.genmodel");
private ResourceSet resourceSet;
private IQualifiedNameConverter nameConverter;
private Provider<EcoreXcoreBuilder> ecoreXcoreBuilderProvider;
private XcoreJvmInferrer jvmInferrer;
public EClassifierScope(IScope parent, ResourceSet resourceSet, IQualifiedNameConverter nameConverter, Provider<EcoreXcoreBuilder> ecoreXcoreBuilderProvider, XcoreJvmInferrer jvmInferrer)
{
super(parent, false);
this.resourceSet = resourceSet;
this.nameConverter = nameConverter;
this.ecoreXcoreBuilderProvider = ecoreXcoreBuilderProvider;
this.jvmInferrer = jvmInferrer;
}
@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 eObjectDescription =
new EcoreDataTypeAliasEObjectDescription(qualifiedName, actualQualifiedName, eDataType);
result.add(eObjectDescription);
}
return result;
}
}
public static Resource getXcoreLangResource(ResourceSet resourceSet)
{
Resource xcoreLangResource = resourceSet.getResource(XAnnotationDirectiveScope.LOGICAL_XCORE_LANG_URI, false);
if (xcoreLangResource == null)
{
xcoreLangResource = resourceSet.getResource(XAnnotationDirectiveScope.PHYSICAL_XCORE_LANG_URI, true);
xcoreLangResource.setURI(XAnnotationDirectiveScope.LOGICAL_XCORE_LANG_URI);
}
return xcoreLangResource;
}
protected static class XAnnotationDirectiveScope extends AbstractScope
{
private static final URI LOGICAL_XCORE_LANG_URI = URI.createURI("platform:/plugin/org.eclipse.emf.ecore.xcore/model/XcoreLang.xcore");
private static final URI PHYSICAL_XCORE_LANG_URI =
EMFPlugin.IS_ECLIPSE_RUNNING ?
LOGICAL_XCORE_LANG_URI :
URI.createURI(XcorePlugin.INSTANCE.getBaseURL().toString() + "model/XcoreLang.xcore");
protected static final String[] IMPLICIT_ANNOTATION_DIRECTIVES =
{
"Ecore", EcorePackage.eNS_URI,
"ExtendedMetaData", ExtendedMetaData.ANNOTATION_URI,
"GenModel", GenModelPackage.eNS_URI
};
private ResourceSet resourceSet;
public XAnnotationDirectiveScope(IScope parent, ResourceSet resourceSet)
{
super(parent, false);
this.resourceSet = resourceSet;
}
@Override
protected Iterable<IEObjectDescription> getAllLocalElements()
{
ArrayList<IEObjectDescription> result = new ArrayList<IEObjectDescription>();
for (int i = 0; i < IMPLICIT_ANNOTATION_DIRECTIVES.length; i += 2)
{
final String name = IMPLICIT_ANNOTATION_DIRECTIVES[i];
final int index = i;
final QualifiedName actualQualifiedName = QualifiedName.create("xcore", "lang",name);
final QualifiedName qualifiedName = QualifiedName.create(name);
AbstractEObjectDescription eObjectDescription =
new AbstractEObjectDescription()
{
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)
{
InternalEObject xAnnotationDirective = (InternalEObject)XcoreFactory.eINSTANCE.createXAnnotationDirective();
xAnnotationDirective.eSetProxyURI(getSyntheticEObjectURI());
return xAnnotationDirective;
}
else
{
return element.getEObjectOrProxy();
}
}
public EClass getEClass()
{
return XcorePackage.Literals.XANNOTATION_DIRECTIVE;
}
protected URI getSyntheticEObjectURI()
{
// TODO
return LOGICAL_XCORE_LANG_URI.appendFragment("/0/@annotationDirectives." + index/2);
}
protected IEObjectDescription getElement()
{
IEObjectDescription element = getParent().getSingleElement(actualQualifiedName);
if (element == null)
{
getXcoreLangResource(resourceSet);
}
return element;
}
};
result.add(eObjectDescription);
}
return result;
}
}
}