/*******************************************************************************
* Copyright (c) 2008,2010 itemis AG (http://www.itemis.eu) 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.mwe2.language.scoping;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.mwe2.language.mwe2.Assignment;
import org.eclipse.emf.mwe2.language.mwe2.BooleanLiteral;
import org.eclipse.emf.mwe2.language.mwe2.Component;
import org.eclipse.emf.mwe2.language.mwe2.DeclaredProperty;
import org.eclipse.emf.mwe2.language.mwe2.Module;
import org.eclipse.emf.mwe2.language.mwe2.Referrable;
import org.eclipse.emf.mwe2.language.mwe2.StringLiteral;
import org.eclipse.emf.mwe2.language.mwe2.Value;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.common.types.JvmFeature;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.Scopes;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
import org.eclipse.xtext.util.SimpleAttributeResolver;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
*
*/
public class Mwe2ScopeProvider extends AbstractDeclarativeScopeProvider {
@Inject
private IInjectableFeatureLookup featureLookup;
@Inject
private FactorySupport factorySupport;
@Inject
private Provider<NameComputation> nameComputationProvider;
public IScope scope_Assignment_feature(Assignment context, EReference reference) {
if (context.eContainer() == null)
throw new IllegalStateException("context.eContainer may not be null");
if (!(context.eContainer() instanceof Component))
throw new IllegalStateException("context.eContainer has to be instance of Component");
Component container = (Component) context.eContainer();
return createComponentFeaturesScope(container);
}
public IScope scope_AbstractReference_referable(StringLiteral owner, EReference reference) {
return createReferenceScopeUpTo(owner.eContainer(), false);
}
public IScope scope_AbstractReference_referable(BooleanLiteral owner, EReference reference) {
return createReferenceScopeUpTo(owner.eContainer(), false);
}
public IScope scope_AbstractReference_referable(DeclaredProperty owner, EReference reference) {
return createReferenceScopeUpTo(owner, true);
}
public IScope scope_AbstractReference_referable(Assignment owner, EReference reference) {
return createReferenceScopeUpTo(owner, true);
}
public IScope createReferenceScopeUpTo(EObject object, boolean allowObjects) {
List<Referrable> result = Lists.newArrayList();
collectReferablesUpTo(object, allowObjects, result);
return createLocalScope(result);
}
public void collectReferablesUpTo(EObject object, boolean allowObjects, List<Referrable> result) {
Module module = EcoreUtil2.getContainerOfType(object, Module.class);
for (DeclaredProperty prop : module.getDeclaredProperties()) {
if (prop == object || prop.getDefault() == object)
return;
if (isAllowed(prop.getDefault(), allowObjects))
result.add(prop);
}
if (allowObjects)
collectReferablesUpTo(module.getRoot(), object, result);
}
protected boolean isAllowed(Value value, boolean allowObjects) {
return allowObjects || !(value instanceof Component); // TODO: discuss
// how we handle
// component
// references in
// strings
}
public boolean collectReferablesUpTo(Component component, EObject object, List<Referrable> result) {
result.add(component);
if (component == object)
return false;
for (Assignment assignment : component.getAssignment()) {
if (assignment == object)
return false;
if (assignment.getValue() instanceof Component) {
if (!collectReferablesUpTo((Component) assignment.getValue(), object, result))
return false;
}
}
return true;
}
public IScope createComponentFeaturesScope(Component container) {
if (container.getModule() != null) {
return createLocalScope(container.getModule().getDeclaredProperties());
} else {
JvmType containerType = container.getActualType();
if (containerType == null || containerType.eIsProxy())
return IScope.NULLSCOPE;
Map<QualifiedName, JvmFeature> features = Maps.newHashMap();
JvmType createType = factorySupport.findFactoriesCreationType(containerType);
if (createType != null) {
features.putAll(featureLookup.getInjectableFeatures(createType));
}
features.putAll(featureLookup.getInjectableFeatures(containerType));
return new MapBasedScope(features);
}
}
public void setFactorySupport(FactorySupport factorySupport) {
this.factorySupport = factorySupport;
}
public void setFeatureLookup(IInjectableFeatureLookup featureLookup) {
this.featureLookup = featureLookup;
}
public IInjectableFeatureLookup getFeatureLookup() {
return featureLookup;
}
protected IScope createLocalScope(List<? extends EObject> elements) {
return Scopes.scopeFor(elements, nameComputationProvider.get(), IScope.NULLSCOPE);
}
protected static class NameComputation implements Function<EObject, QualifiedName> {
@Inject
private IQualifiedNameConverter qualifiedNameConverter;
private SimpleAttributeResolver<EObject, String> nameResolver = SimpleAttributeResolver.newResolver(String.class, "name");
public QualifiedName apply(EObject from) {
String name = nameResolver.apply(from);
if (name != null && name.length() > 0)
return qualifiedNameConverter.toQualifiedName(nameResolver.apply(from));
return null;
}
}
}