/*******************************************************************************
* 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.avm.scoping;
import java.util.Iterator;
import java.util.List;
import org.axdt.avm.access.DefinitionNotFoundException;
import org.axdt.avm.model.AvmClass;
import org.axdt.avm.model.AvmDeclaredType;
import org.axdt.avm.model.AvmExecutable;
import org.axdt.avm.model.AvmInterface;
import org.axdt.avm.model.AvmMember;
import org.axdt.avm.model.AvmReferable;
import org.axdt.avm.model.AvmType;
import org.axdt.avm.model.AvmTypeReference;
import org.axdt.avm.naming.AvmQualifiedNameConverter;
import org.axdt.avm.util.AvmTypeAccess;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.EObjectDescription;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.scoping.impl.AbstractScope;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
public abstract class AvmElementScope<T extends EObject> extends AbstractScope {
public final static Function<AvmReferable, IEObjectDescription> GetDesciption = new Function<AvmReferable, IEObjectDescription>() {
public IEObjectDescription apply(AvmReferable from) {
String name = from.getName();
return name != null ? EObjectDescription.create(name, from) : null;
}
};
protected final T element;
protected final EReference ref;
protected AvmDeclaredType typeEnclosingElement;
protected final IScope lookup;
public AvmElementScope(IScope scope, T element, EReference ref, IScope lookup) {
super(scope, false);
this.element = element;
this.ref = ref;
this.lookup = lookup;
}
public IScope getLookupScope() {
return lookup;
}
@Override
protected Iterable<IEObjectDescription> getAllLocalElements() {
return Iterables.filter(
getCandidates(),
Predicates.notNull());
}
protected abstract Iterable<IEObjectDescription> getCandidates();
protected AvmType resolveType(AvmType type, AvmTypeReference ref) {
if (type.eIsProxy()) {
InternalEObject internal = (InternalEObject) type;
String urlString = internal.eProxyURI().toString();
if (urlString.startsWith("avm:/lookup/")) {
String lookupName = urlString.replaceFirst("avm:/lookup/", "");
// lets lookup the type name in the parent scope
IScope current = getLookupScope();
AvmLibraryScope avmScope = null;
while (current != null) {
if (current instanceof AvmLibraryScope) {
avmScope = (AvmLibraryScope) current;
break;
}
if (current instanceof AbstractScope)
current = ((AbstractScope)current).getParent();
else break;
}
if (avmScope != null) {
try {
QualifiedName qname = new AvmQualifiedNameConverter().toQualifiedName(lookupName);
Iterable<IEObjectDescription> desciptions = avmScope.getTypedElementsByName(qname, type.eClass());
Iterator<IEObjectDescription> iterator = desciptions.iterator();
if (iterator.hasNext()) {
IEObjectDescription next = iterator.next();
EObject object = next.getEObjectOrProxy();
// resolve if proxy
if (object.eIsProxy())
object = EcoreUtil2.resolve(object, element);
type = (AvmType) object;
// set the canonical type name so we can skip the scope lookup next time
internal.eSetProxyURI(URI.createURI("avm:/types/"+ type.getCanonicalName()));
if (ref != null) {
Resource resource = ref.eResource();
if (resource != null) {
resource.setModified(true);
}
}
}
} catch (DefinitionNotFoundException e) {
}
}
} else {
type = (AvmType) EcoreUtil2.resolve(type, element);
}
}
return type;
}
protected void collectAllMembers(AvmTypeAccess access, List<AvmMember> list,
boolean intMod, boolean first) {
AvmDeclaredType type = (AvmDeclaredType) access.getType();
for (AvmMember member : type.getMembers()) {
if (member instanceof AvmExecutable && type.getName() != null && type.getName().equals(member.getName())) {
// skip constructor for now
// we can work with type references in new expressions
// and we usually want to access the type instead of the constructor
// we can lookup the constructor on demand when we need parameter information
continue;
}
if (member.isStatic()) {
if (!access.isStatic())
continue;
} else {
if (!access.isInstance())
continue;
}
switch (member.getVisibility()) {
case PUBLIC:
break;
case PROTECTED:
if (access.isProtected()) break;
continue;
case PRIVATE:
if (access.isPrivate()) break;
continue;
case INTERNAL:
if (intMod) break;
continue;
}
list.add(member);
}
if (type.isClass()) {
AvmClass clss = (AvmClass) type;
AvmTypeReference extendedClass = clss.getExtendedClass();
if (extendedClass == null && !type.getCanonicalName().equals("Object"))
extendedClass = getObjectReference();
AvmDeclaredType superType = resolveTypeReference(extendedClass);
if (superType instanceof AvmClass) {
AvmTypeAccess newaccess = AvmTypeAccess.Factory.access(superType)
.setProtected(access.isProtected())
.setInstance(access.isInstance())
.setStatic(access.isStatic());
boolean newIntMod = sameQualifier(superType, getTypeEnclosingElement());
collectAllMembers(newaccess, list, newIntMod, false);
}
}
List<AvmTypeReference> interfaces = type.getExtendedInterfaces();
for (AvmTypeReference ref:interfaces) {
AvmDeclaredType superType = resolveTypeReference(ref);
if (superType instanceof AvmInterface) {
AvmTypeAccess newaccess = AvmTypeAccess.Factory.access(superType);
collectAllMembers(newaccess, list, false, false);
}
}
}
protected abstract AvmTypeReference getObjectReference();
protected boolean sameQualifier(AvmDeclaredType t1, AvmDeclaredType t2) {
if (t2 == null) return false;
String qualifier = t1.getQualifier();
if (qualifier == null) {
if (t2.getQualifier() == null)
return true;
} else if (qualifier.equals(t2.getQualifier())) {
return true;
}
return false;
}
protected Iterable<AvmMember> getAllMembers(AvmDeclaredType type, AvmTypeAccess access) {
AvmTypeAccess newAccess = AvmTypeAccess.Factory.access(type);
boolean intMod = false;
AvmDeclaredType refType = getTypeEnclosingElement();
intMod = sameQualifier(type, refType);
if (access != null) {
newAccess.setProtected(access.isProtected());
newAccess.setPrivate(access.isPrivate());
newAccess.setStatic(access.isStatic());
newAccess.setInstance(access.isInstance());
}
if (intMod && type.getName().equals(refType.getName())) {
newAccess.setProtected(true);
newAccess.setPrivate(true);
}
List<AvmMember> result = Lists.newArrayList();
collectAllMembers(newAccess, result, intMod, true);
return result;
}
protected AvmDeclaredType getTypeEnclosingElement() {
if (typeEnclosingElement == null) {
for (EObject current = element; current != null; current = current.eContainer()) {
if (current instanceof AvmDeclaredType) {
typeEnclosingElement = (AvmDeclaredType) current;
break;
}
}
}
return typeEnclosingElement;
}
protected AvmDeclaredType resolveTypeReference(AvmTypeReference ref) {
if (ref != null) {
AvmType parent = resolveType(ref.getType(), ref);
if (parent instanceof AvmDeclaredType) {
return (AvmDeclaredType) parent;
}
}
return null;
}
protected boolean isShadowed(IEObjectDescription input) {
QualifiedName name = input.getName();
int count = name.getSegmentCount();
if (count < 1 || count > 1)
return false;
return getLocalElementsByName(name).iterator().hasNext();
}
}