org.eclipse.xtext.scoping.impl.AbstractScope Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 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.xtext.scoping.impl;
import static com.google.common.collect.Iterables.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.inject.Provider;
/**
* Base class for custom scope implementations. It supports nesting of scopes
* into each other, appropriate shadowing semantics and case sensitive and insensitive
* lookup.
*
* Implementors have to provide {@link #getAllLocalElements()}. However, it is recommended
* to customize {@link #getLocalElementsByEObject(EObject, URI)} and {@link #getLocalElementsByName(QualifiedName)}
* as well.
*
* @author Sven Efftinge - Initial contribution and API
* @author Sebastian Zarnekow
*/
public abstract class AbstractScope implements IScope {
/**
* Lazy iterable with a reasonable {@link #toString()} implementation that supports
* shadowing of parents elements by means of filtering.
*/
protected static class ParentIterable implements Iterable, Predicate {
private final AbstractScope scope;
private final Provider> provider;
private Iterable parentElements;
protected ParentIterable(AbstractScope scope, Provider> provider) {
this.scope = scope;
this.provider = provider;
}
public Iterator iterator() {
if (parentElements == null) {
parentElements = provider.get();
}
Iterator parentIterator = parentElements.iterator();
Iterator filteredIterator = Iterators.filter(parentIterator, this);
return filteredIterator;
}
public boolean apply(IEObjectDescription input) {
return !scope.isShadowed(input);
}
@Override
public String toString() {
return Iterables.toString(this);
}
}
private final boolean ignoreCase;
private final IScope parent;
/**
* Creates a new scope with a given parent.
* @param parent the parent scope. May not be null
. Use {@link IScope#NULLSCOPE NULLSCOPE} instead.
* @param ignoreCase whether name lookup and shadowing should be case insensitive or not.
*/
protected AbstractScope(IScope parent, boolean ignoreCase) {
if (parent == null)
throw new IllegalArgumentException("parent may not be null. Use IScope.NULLSCOPE instead.");
this.parent = parent;
this.ignoreCase = ignoreCase;
}
public IScope getParent() {
return parent;
}
public boolean isIgnoreCase() {
return ignoreCase;
}
public IEObjectDescription getSingleElement(QualifiedName name) {
IEObjectDescription result = getSingleLocalElementByName(name);
if (result != null)
return result;
return getParent().getSingleElement(name);
}
protected IEObjectDescription getSingleLocalElementByName(QualifiedName name) {
Iterable result = getLocalElementsByName(name);
Iterator iterator = result.iterator();
if (iterator.hasNext())
return iterator.next();
return null;
}
public Iterable getAllElements() {
Iterable localElements = getAllLocalElements();
Iterable parentElements = getParentElements(new Provider>() {
public Iterable get() {
return getParent().getAllElements();
}
});
Iterable result = Iterables.concat(localElements, parentElements);
return result;
}
public Iterable getElements(final QualifiedName name) {
Iterable localElements = getLocalElementsByName(name);
if (localElements instanceof Collection) {
if (((Collection>) localElements).isEmpty())
return getParent().getElements(name);
}
Iterable parentElements = getParentElements(new Provider>() {
public Iterable get() {
return getParent().getElements(name);
}
});
Iterable result = Iterables.concat(localElements, parentElements);
return result;
}
public IEObjectDescription getSingleElement(EObject object) {
Iterable elements = getElements(object);
Iterator iterator = elements.iterator();
if (iterator.hasNext()) {
IEObjectDescription result = iterator.next();
return result;
}
return null;
}
public Iterable getElements(final EObject object) {
final URI uri = EcoreUtil2.getNormalizedURI(object);
Iterable localElements = getLocalElementsByEObject(object, uri);
Iterable parentElements = getParentElements(new Provider>() {
public Iterable get() {
return getParent().getElements(object);
}
});
Iterable result = Iterables.concat(localElements, parentElements);
return result;
}
protected abstract Iterable getAllLocalElements();
protected Iterable getLocalElementsByName(final QualifiedName name) {
Iterable localElements = getAllLocalElements();
Iterable result = Iterables.filter(localElements, new Predicate() {
public boolean apply(IEObjectDescription input) {
if (isIgnoreCase()) {
QualifiedName lowerCase = name.toLowerCase();
QualifiedName inputLowerCase = input.getName().toLowerCase();
return lowerCase.equals(inputLowerCase);
} else {
return name.equals(input.getName());
}
}
});
return result;
}
protected Iterable getLocalElementsByEObject(final EObject object, final URI uri) {
Iterable localElements = getAllLocalElements();
Iterable result = Iterables.filter(localElements, new Predicate() {
public boolean apply(IEObjectDescription input) {
if (input.getEObjectOrProxy() == object)
return canBeFoundByName(input);
if (uri.equals(input.getEObjectURI())) {
return canBeFoundByName(input);
}
return false;
}
public boolean canBeFoundByName(IEObjectDescription input) {
IEObjectDescription lookUp = getSingleLocalElementByName(input.getName());
if (lookUp != null) {
if (lookUp == input)
return true;
if (lookUp.getEObjectOrProxy() == object)
return true;
if (uri.equals(lookUp.getEObjectURI()))
return true;
}
return false;
}
});
return result;
}
protected Iterable getParentElements(Provider> provider) {
if (getParent() == IScope.NULLSCOPE)
return Collections.emptyList();
return new ParentIterable(this, provider);
}
/**
* Returns true
if the given description {@code input} from the parent scope is
* shadowed by local elements.
* @return true
if the given description {@code input} from the parent scope is
* shadowed by local elements.
*/
protected boolean isShadowed(IEObjectDescription input) {
final Iterable localElements = getLocalElementsByName(input.getName());
final boolean isEmpty = isEmpty(localElements);
return !isEmpty;
}
@Override
public String toString() {
String parentString = null;
try {
final IScope parent = getParent();
parentString = parent.toString();
} catch (Throwable t) {
parentString = t.getClass().getSimpleName() + " : " + t.getMessage();
}
return getClass().getSimpleName() + (ignoreCase? "[ignore case]":"") + getAllLocalElements() + " -> " + parentString;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy