/*******************************************************************************
* Copyright (c) 2010 Martin Schnabel <mb0@mb0.org>.
* 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.axdt.as3.scoping;
import java.lang.reflect.Method;
import java.util.Collections;
import org.apache.log4j.Logger;
import org.axdt.as3.As3EFactory;
import org.axdt.as3.As3EPackage;
import org.axdt.as3.model.As3AccessExpression;
import org.axdt.as3.model.As3AttributeQualifiedIdentifier;
import org.axdt.as3.model.As3CatchClause;
import org.axdt.as3.model.As3Class;
import org.axdt.as3.model.As3Executable;
import org.axdt.as3.model.As3ExpressionList;
import org.axdt.as3.model.As3FieldBinding;
import org.axdt.as3.model.As3Program;
import org.axdt.as3.model.As3PropertyIdentifier;
import org.axdt.as3.model.As3PropertyOperator;
import org.axdt.as3.model.As3QueryExpression;
import org.axdt.as3.model.As3QueryOperator;
import org.axdt.as3.model.As3WithStatement;
import org.axdt.as3.model.IExpression;
import org.axdt.as3.model.IIdentifier;
import org.axdt.asdoc.AsdocEFactory;
import org.axdt.asdoc.model.AsdocClass;
import org.axdt.avm.AvmEFactory;
import org.axdt.avm.model.AvmDeclaredType;
import org.axdt.avm.model.AvmDeclaredTypeReference;
import org.axdt.avm.model.AvmField;
import org.axdt.avm.model.AvmReferable;
import org.axdt.avm.model.AvmTypeReference;
import org.axdt.avm.scoping.AvmElementScope;
import org.axdt.avm.scoping.AvmGenericScope;
import org.axdt.avm.scoping.AvmPropertyScope;
import org.axdt.avm.scoping.AvmTypeScope;
import org.axdt.avm.util.AvmTypeAccess;
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.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.IScopeProvider;
import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
import org.eclipse.xtext.scoping.impl.AbstractScopeProvider;
import org.eclipse.xtext.util.PolymorphicDispatcher;
import org.eclipse.xtext.util.Tuples;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
import com.google.inject.name.Named;
/**
* This class contains custom scoping description.
*
* @author mb0 see :
* http://www.eclipse.org/Xtext/documentation/latest/xtext.html#scoping
* on
*/
public class As3ScopeProvider extends AbstractScopeProvider {
public final Logger logger = Logger.getLogger(getClass());
@Inject
@Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
private IScopeProvider delegate;
@Inject(optional = true)
@Named(AbstractDeclarativeScopeProvider.NAMED_ERROR_HANDLER)
private PolymorphicDispatcher.ErrorHandler<IScope> errorHandler = new PolymorphicDispatcher.NullErrorHandler<IScope>();
protected IScope delegateGetScope(EObject context, EReference reference) {
return getDelegate().getScope(context, reference);
}
public void setDelegate(IScopeProvider delegate) {
this.delegate = delegate;
}
public IScopeProvider getDelegate() {
return delegate;
}
public void setErrorHandler(
PolymorphicDispatcher.ErrorHandler<IScope> errorHandler) {
this.errorHandler = errorHandler;
}
public PolymorphicDispatcher.ErrorHandler<IScope> getErrorHandler() {
return errorHandler;
}
protected Predicate<Method> getPredicate(EObject context, EClass type) {
String methodName = "scope_" + type.getName();
return PolymorphicDispatcher.Predicates.forName(methodName, 3);
}
public IScope getScope(EObject context, EReference reference) {
return getScope(context, context, reference);
}
public IScope getScope(EObject context, EObject current,
EReference reference) {
IScope scope = polymorphicFindScopeForClassName(context, current,
reference);
if (scope == null)
scope = delegateGetScope(current, reference);
return scope;
}
private IScope polymorphicFindScopeForClassName(EObject context,
EObject current, EReference reference) {
IScope scope = null;
PolymorphicDispatcher<IScope> dispatcher = new PolymorphicDispatcher<IScope>(
Collections.singletonList(this), getPredicate(context,
reference.getEReferenceType()), errorHandler);
while (scope == null && current != null) {
scope = dispatcher.invoke(current, context, reference);
current = current.eContainer();
}
return scope;
}
Object getCacheKey(EObject ctx, EReference ref) {
return Tuples.create(As3ScopeProvider.class, ctx, ref);
}
IScope scope_AvmType(AvmTypeReference ctx, EObject initial, EReference ref) {
// TODO: what is with package local types
return delegateGetScope(ctx, ref);
}
IScope scope_AvmReferable(As3Program ctx, EObject initial, EReference ref) {
return new As3ProgramScope(delegateGetScope(ctx, ref), ctx, ref);
}
IScope scope_AvmReferable(As3Class ctx, EObject initial, EReference ref) {
return new As3TypeScope(getScope(ctx, ctx.eContainer(), ref), ctx,
initial, ref);
}
IScope scope_AvmReferable(As3Executable ctx, EObject initial, EReference ref) {
return new As3ExecutableScope(getScope(ctx, ctx.eContainer(), ref),
ctx, ref);
}
IScope scope_AvmReferable(final As3WithStatement ctx,
final EObject initial, final EReference ref) {
EObject current = initial;
while (current != null) {
if (ctx.equals(current.eContainer())) {
if (current.eContainingFeature() == As3EPackage.eINSTANCE
.getAs3WithStatement_Target())
return null;
}
current = current.eContainer();
}
IScope parent = getScope(ctx, ctx.eContainer(), ref);
return new As3WithScope(parent, ctx,
initial, ref, parent);
}
IScope scope_AvmReferable(As3CatchClause ctx, EObject initial,
EReference ref) {
return new As3CatchScope(getScope(ctx, ctx.eContainer(), ref), ctx, ref);
}
IScope scope_AvmReferable(As3PropertyOperator ctx, EObject initial,
EReference ref) {
if (ctx.getExpressions() == null
&& ctx.eContainingFeature() == As3EPackage.eINSTANCE
.getAs3AccessExpression_Operator()) {
As3AccessExpression access = (As3AccessExpression) ctx.eContainer();
return new As3PropertyScope(IScope.NULLSCOPE, access, ref, delegateGetScope(ctx, ref));
}
return null;
}
IScope scope_AvmReferable(As3QueryOperator ctx, EObject initial,
EReference ref) {
if (ctx.eContainingFeature() == As3EPackage.eINSTANCE
.getAs3QueryExpression_Operator()
&& initial instanceof As3PropertyIdentifier) {
As3QueryExpression access = (As3QueryExpression) ctx.eContainer();
return new As3QueryScope(IScope.NULLSCOPE, access, ref, initial, delegateGetScope(ctx, ref));
}
return null;
}
}
class As3TypeScope extends AvmTypeScope {
public As3TypeScope(IScope parent, AvmDeclaredType element,
EObject initial, EReference ref) {
super(parent, element, initial, ref);
}
@Override
protected AvmTypeReference getObjectReference() {
return As3ObjectReferenceProvider.get();
}
}
class As3ProgramScope extends AvmElementScope<As3Program> {
public As3ProgramScope(IScope parent, As3Program element, EReference ref) {
super(parent, element, ref, parent);
}
@Override
protected Iterable<IEObjectDescription> getCandidates() {
return Iterables.transform(
Iterables.concat(element.getMembers(), element.getTypes()),
GetDesciption);
}
@Override
protected AvmTypeReference getObjectReference() {
return As3ObjectReferenceProvider.get();
}
}
class As3ExecutableScope extends AvmElementScope<As3Executable> {
public As3ExecutableScope(IScope parent, As3Executable element,
EReference ref) {
super(parent, element, ref, parent);
}
@Override
protected Iterable<IEObjectDescription> getCandidates() {
return Iterables.transform(
Iterables.concat(element.getParameters(),
element.getDeclarations()), GetDesciption);
}
@Override
protected AvmTypeReference getObjectReference() {
return As3ObjectReferenceProvider.get();
}
}
class As3QueryScope extends As3GenericScope<As3QueryExpression> {
private final EObject ident;
public As3QueryScope(IScope parent, As3QueryExpression element,
EReference ref, EObject ident, IScope lookup) {
super(parent, element, ref, lookup);
this.ident = ident;
}
protected EObject getReference() {
return ident;
}
@Override
protected AvmTypeReference getObjectReference() {
return As3ObjectReferenceProvider.get();
}
}
abstract class As3GenericScope<T extends IExpression> extends
AvmGenericScope<T> {
public As3GenericScope(IScope parent, T element, EReference ref, IScope lookup) {
super(parent, element, ref, lookup);
}
@Override
protected Iterable<IEObjectDescription> getCandidates() {
EObject identifier = getReference();
if (identifier instanceof As3AttributeQualifiedIdentifier)
identifier = ((As3AttributeQualifiedIdentifier) identifier)
.getIdentifier();
if (identifier instanceof As3PropertyIdentifier) {
AvmReferable dynIdent = getDynamicIdentifiable(identifier);
if (dynIdent != null)
return Collections.singleton(GetDesciption.apply(dynIdent));
}
return Collections.emptySet();
}
protected abstract EObject getReference();
protected AvmField createDynamicField(String name) {
As3FieldBinding field = As3EFactory.eINSTANCE.createAs3FieldBinding();
field.setName(name);
field.setType(AvmEFactory.eINSTANCE.createAvmGenericReference());
return field;
}
}
class As3PropertyScope extends AvmPropertyScope<As3AccessExpression> {
public As3PropertyScope(IScope parent, As3AccessExpression element,
EReference ref, IScope lookup) {
super(parent, element, ref, lookup);
}
@Override
protected AvmTypeAccess getQualifierType() {
return element.getExpression().resolveType();
}
@Override
protected EObject getReference() {
IIdentifier id = element.getOperator().getIdentifier();
if (id instanceof As3AttributeQualifiedIdentifier) {
return ((As3AttributeQualifiedIdentifier) id).getIdentifier();
} else if (id instanceof As3PropertyIdentifier) {
return id;
}
return null;
}
@Override
protected AvmField createDynamicField(String name) {
As3FieldBinding field = As3EFactory.eINSTANCE.createAs3FieldBinding();
field.setName(name);
field.setType(AvmEFactory.eINSTANCE.createAvmGenericReference());
return field;
}
@Override
protected AvmTypeReference getObjectReference() {
return As3ObjectReferenceProvider.get();
}
}
class As3WithScope extends AvmPropertyScope<As3WithStatement> {
protected final EObject ctx;
public As3WithScope(IScope parent, As3WithStatement element,
EObject initial, EReference ref, IScope lookup) {
super(parent, element, ref, lookup);
this.ctx = initial;
}
@Override
protected AvmTypeAccess getQualifierType() {
As3ExpressionList target = element.getTarget();
EList<IExpression> list = target.getExpressions();
if (!list.isEmpty()) {
IExpression expression = list.get(list.size() - 1);
return expression.resolveType();
}
return AvmTypeAccess.NULL;
}
@Override
protected EObject getReference() {
if (ctx instanceof As3AttributeQualifiedIdentifier) {
return ((As3AttributeQualifiedIdentifier) ctx).getIdentifier();
} else if (ctx instanceof As3PropertyIdentifier) {
return ctx;
}
return null;
}
@Override
protected AvmField createDynamicField(String name) {
As3FieldBinding field = As3EFactory.eINSTANCE.createAs3FieldBinding();
field.setName(name);
field.setType(AvmEFactory.eINSTANCE.createAvmGenericReference());
return field;
}
@Override
protected AvmTypeReference getObjectReference() {
return As3ObjectReferenceProvider.get();
}
}
class As3CatchScope extends AvmElementScope<As3CatchClause> {
public As3CatchScope(IScope parent, As3CatchClause element, EReference ref) {
super(parent, element, ref, parent);
}
@Override
protected Iterable<IEObjectDescription> getCandidates() {
return Collections.singleton(GetDesciption.apply(element.getError()));
}
@Override
protected AvmTypeReference getObjectReference() {
return As3ObjectReferenceProvider.get();
}
}
class As3ObjectReferenceProvider {
public static AvmTypeReference get() {
AvmDeclaredTypeReference ref = AvmEFactory.eINSTANCE.createAvmDeclaredTypeReference();
AsdocClass proxy = AsdocEFactory.eINSTANCE.createAsdocClass();
((InternalEObject) proxy).eSetProxyURI(URI.createURI("avm:/types/Object"));
ref.setType(proxy);
return ref;
}
}