org.eclipse.xtext.linking.lazy.LazyLinker Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2008 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.linking.lazy;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EPackage.Registry;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.xtext.AbstractMetamodelDeclaration;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.diagnostics.IDiagnosticConsumer;
import org.eclipse.xtext.diagnostics.IDiagnosticProducer;
import org.eclipse.xtext.linking.impl.AbstractCleaningLinker;
import org.eclipse.xtext.linking.impl.LinkingDiagnosticProducer;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.util.EcoreGenericsUtil;
import org.eclipse.xtext.util.OnChangeEvictingCache;
import org.eclipse.xtext.util.SimpleCache;
import org.eclipse.xtext.util.concurrent.IUnitOfWork;
import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
/**
* @author Sven Efftinge - Initial contribution and API
* @author Knut Wannheden
*/
public class LazyLinker extends AbstractCleaningLinker {
private static final Logger log = Logger.getLogger(LazyLinker.class);
private SimpleCache instantiableSubTypes = new SimpleCache(
new Function() {
public EClass apply(EClass from) {
return findInstantiableCompatible(from);
}
});
@Inject
private LazyURIEncoder encoder;
@Inject
private Registry registry;
@Inject
private EcoreGenericsUtil ecoreGenericsUtil;
@Inject
private IGrammarAccess grammarAccess;
@Inject
private OnChangeEvictingCache cache;
@Override
protected void doLinkModel(final EObject model, IDiagnosticConsumer consumer) {
final Multimap settingsToLink = ArrayListMultimap.create();
final LinkingDiagnosticProducer producer = new LinkingDiagnosticProducer(consumer);
cache.execWithoutCacheClear(model.eResource(), new IUnitOfWork.Void() {
@Override
public void process(Resource state) throws Exception {
installProxies(model, producer, settingsToLink);
TreeIterator iterator = model.eAllContents();
while (iterator.hasNext()) {
EObject eObject = iterator.next();
installProxies(eObject, producer, settingsToLink);
}
}
});
installQueuedLinks(settingsToLink);
}
protected void installProxies(EObject obj, IDiagnosticProducer producer,
Multimap settingsToLink) {
ICompositeNode node = NodeModelUtils.getNode(obj);
if (node == null)
return;
installProxies(obj, producer, settingsToLink, node);
}
private void installProxies(EObject obj, IDiagnosticProducer producer,
Multimap settingsToLink, ICompositeNode parentNode) {
Iterator iterator = parentNode.getChildren().iterator();
while(iterator.hasNext()) {
INode node = iterator.next();
if (node.getGrammarElement() instanceof CrossReference && !Iterables.isEmpty(node.getLeafNodes())) {
CrossReference ref = (CrossReference) node.getGrammarElement();
producer.setNode(node);
final EReference eRef = GrammarUtil.getReference(ref, obj.eClass());
if (eRef == null) {
throw new IllegalStateException("Couldn't find EReference for crossreference " + ref);
}
if (!eRef.isResolveProxies() /*|| eRef.getEOpposite() != null see https://bugs.eclipse.org/bugs/show_bug.cgi?id=282486*/) {
final EStructuralFeature.Setting setting = ((InternalEObject) obj).eSetting(eRef);
settingsToLink.put(new SettingDelegate(setting), node);
} else {
createAndSetProxy(obj, node, eRef);
}
}
}
if (shouldCheckParentNode(parentNode)) {
installProxies(obj, producer, settingsToLink, parentNode.getParent());
}
}
@SuppressWarnings("unchecked")
protected void installQueuedLinks(Multimap settingsToLink) {
for (EStructuralFeature.Setting setting : settingsToLink.keySet()) {
final EObject eObject = setting.getEObject();
final EReference eRef = (EReference) setting.getEStructuralFeature();
final Collection nodes = settingsToLink.get(setting);
if (setting.getEStructuralFeature().isMany()) {
EList list = (EList) setting.get(false);
for (INode node : nodes) {
final EObject proxy = createProxy(eObject, node, eRef);
list.add(EcoreUtil.resolve(proxy, eObject));
}
} else {
final INode node = nodes.iterator().next();
final EObject proxy = createProxy(eObject, node, eRef);
setting.set(EcoreUtil.resolve(proxy, eObject));
}
}
}
@SuppressWarnings("unchecked")
protected void createAndSetProxy(EObject obj, INode node, EReference eRef) {
final EObject proxy = createProxy(obj, node, eRef);
if (eRef.isMany()) {
((InternalEList) obj.eGet(eRef, false)).addUnique(proxy);
} else {
obj.eSet(eRef, proxy);
}
}
protected EObject createProxy(EObject obj, INode node, EReference eRef) {
final Resource resource = obj.eResource();
if (resource == null)
throw new IllegalStateException("object must be contained in a resource");
final URI uri = resource.getURI();
final URI encodedLink = uri.appendFragment(encoder.encode(obj, eRef, node));
EClass referenceType = ecoreGenericsUtil.getReferenceType(eRef, obj.eClass());
EClass instantiableType = instantiableSubTypes.get(referenceType);
final EObject proxy = EcoreUtil.create(instantiableType);
((InternalEObject) proxy).eSetProxyURI(encodedLink);
return proxy;
}
protected EClass findInstantiableCompatible(EClass eType) {
if (!isInstantiatableSubType(eType, eType)) {
// check local Package
EPackage ePackage = eType.getEPackage();
EClass eClass = findSubTypeInEPackage(ePackage, eType);
if (eClass != null)
return eClass;
return globalFindInstantiableCompatible(eType);
}
return eType;
}
protected EClass globalFindInstantiableCompatible(EClass eType) {
Set visitedPackages = Sets.newHashSet(eType.getEPackage().getNsURI());
for(AbstractMetamodelDeclaration metamodel: GrammarUtil.allMetamodelDeclarations(grammarAccess.getGrammar())) {
if (visitedPackages.add(metamodel.getEPackage().getNsURI())) {
EClass result = findSubTypeInEPackage(metamodel.getEPackage(), eType);
if (result != null)
return result;
}
}
log.warn("Traversing EPackage registry to find instantiable subtype for '" + eType.getName() + "'");
log.warn("You may override LazyLinker#globalFindInstantiableCompatible(..) to prevent this.");
for (String nsURI : getRegisteredNsUris()) {
if (visitedPackages.add(nsURI)) {
try {
EClass result = findSubTypeInEPackage(getRegistry().getEPackage(nsURI), eType);
if (result != null)
return result;
} catch(WrappedException ex) {
log.error("Error when loading EPackage '" + nsURI + "'");
log.error("You may override LazyLinker#globalFindInstantiableCompatible(..) to prevent this.");
log.error("Error when loading EPackage '" + nsURI + "'", ex);
}
}
}
throw new IllegalStateException(
"Could not find an instantiable subtype for type: '" + eType.getName() + "' (" + eType.getEPackage().getNsURI() + ").");
}
private List getRegisteredNsUris() {
Set keySet = getRegistry().keySet();
// copy to avoid ConcurrentModificationException while iterating over the EPackageMap
List copy = Lists.newArrayList(keySet);
return copy;
}
protected EClass findSubTypeInEPackage(EPackage ePackage, EClass superType) {
EList classifiers = ePackage.getEClassifiers();
for (EClassifier eClassifier : classifiers) {
if (eClassifier instanceof EClass) {
EClass c = (EClass) eClassifier;
if (isInstantiatableSubType(c, superType))
return c;
}
}
return null;
}
private boolean isInstantiatableSubType(EClass c, EClass superType) {
return !c.isAbstract() && !c.isInterface() && EcoreUtil2.isAssignableFrom(superType, c);
}
public LazyURIEncoder getEncoder() {
return encoder;
}
public Registry getRegistry() {
return registry;
}
public void setRegistry(Registry registry) {
this.registry = registry;
}
public void setEncoder(LazyURIEncoder encoder) {
this.encoder = encoder;
}
public void setGrammarAccess(IGrammarAccess grammarAccess) {
this.grammarAccess = grammarAccess;
}
public IGrammarAccess getGrammarAccess() {
return grammarAccess;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy