[xtext][grammar] Fix: Grammar linking is broken for imported packages for a clean build

see https://bugs.eclipse.org/bugs/show_bug.cgi?id=364446
This commit is contained in:
Sebastian Zarnekow 2011-11-22 16:41:47 +01:00
parent 280cb4c820
commit a69606b307
10 changed files with 207 additions and 29 deletions

View file

@ -17,13 +17,18 @@ import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.parser.antlr.IReferableElementsUnloader;
import org.eclipse.xtext.parsetree.reconstr.ITokenSerializer.ICrossReferenceSerializer;
import org.eclipse.xtext.parsetree.reconstr.ITransientValueService;
import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager;
import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
import org.eclipse.xtext.resource.IDerivedStateComputer;
import org.eclipse.xtext.resource.IFragmentProvider;
import org.eclipse.xtext.resource.ILocationInFileProvider;
import org.eclipse.xtext.resource.IResourceDescription;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.scoping.IGlobalScopeProvider;
import org.eclipse.xtext.scoping.IScopeProvider;
import org.eclipse.xtext.scoping.impl.DefaultGlobalScopeProvider;
import org.eclipse.xtext.validation.IDiagnosticConverter;
import org.eclipse.xtext.xtext.GrammarResource;
import org.eclipse.xtext.xtext.XtextCrossReferenceSerializer;
import org.eclipse.xtext.xtext.XtextDiagnosticConverter;
import org.eclipse.xtext.xtext.XtextFormatter;
@ -132,4 +137,22 @@ public class XtextRuntimeModule extends AbstractXtextRuntimeModule {
return DefaultGlobalScopeProvider.class;
}
@Override
public Class<? extends XtextResource> bindXtextResource() {
return GrammarResource.class;
}
/**
* @since 2.2
*/
public Class<? extends IDerivedStateComputer> bindIDerivedStateComputer() {
return GrammarResource.LinkingTrigger.class;
}
/**
* @since 2.2
*/
public Class<? extends IResourceDescription.Manager> bindIResourceDescriptionManager() {
return DerivedStateAwareResourceDescriptionManager.class;
}
}

View file

@ -11,12 +11,13 @@ import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.util.IResourceScopeCache;
import org.eclipse.xtext.util.OnChangeEvictingCache;
import com.google.inject.Inject;
/**
*
* Adds a hook for late initialization to be used to create derived state
* Adds a hook for late initialization to be used to create derived state.
*
* @author Sven Efftinge - Initial contribution and API
* @since 2.1
@ -33,6 +34,12 @@ public class DerivedStateAwareResource extends LazyLinkingResource {
protected volatile boolean fullyInitialized = false;
protected volatile boolean isInitializing = false;
/**
* {@inheritDoc}
* <p>
* As soon as an external client tries to access the content of the resource,
* the {@link IDerivedStateComputer derived state} will be added to the content of this resource.
*/
@Override
public synchronized EList<EObject> getContents() {
if (isLoaded && !isLoading && !isInitializing && !isUpdating && !fullyInitialized) {
@ -53,6 +60,48 @@ public class DerivedStateAwareResource extends LazyLinkingResource {
}
super.updateInternalState(oldParseResult, newParseResult);
}
/**
* Overridden to make sure that the cache is initialized during {@link #isLoading() loading}.
*/
@Override
protected void updateInternalState(IParseResult newParseResult) {
super.updateInternalState(newParseResult);
// make sure that the cache adapter is installed on this resource
IResourceScopeCache cache = getCache();
if (cache instanceof OnChangeEvictingCache) {
((OnChangeEvictingCache) cache).getOrCreate(this);
}
}
/**
* {@inheritDoc}
* <p>
* Overridden to make sure that we do not initialize a resource
* just to compute the root URI fragment for the parse result.
*/
@Override
protected String getURIFragmentRootSegment(EObject eObject) {
if (unloadingContents == null) {
IParseResult parseResult = getParseResult();
if (parseResult != null && eObject == parseResult.getRootASTElement()) {
return "0";
}
}
return super.getURIFragmentRootSegment(eObject);
}
/**
* {@inheritDoc}
* <p>
* <em>Not</em> specialized because we want to obtain a fully working root instance
* when the resource is queried with the root fragment.
*/
@Override
protected EObject getEObjectForURIFragmentRootSegment(String uriFragmentRootSegment) {
return super.getEObjectForURIFragmentRootSegment(uriFragmentRootSegment);
}
public void discardDerivedState() {
if (fullyInitialized && !isInitializing) {

View file

@ -0,0 +1,83 @@
/*******************************************************************************
* Copyright (c) 2011 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.xtext;
import org.eclipse.emf.common.util.EList;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.resource.DerivedStateAwareResource;
import org.eclipse.xtext.resource.IDerivedStateComputer;
/**
* Resource implementation that instantiates the infered packages as part of the
* derived state computation.
*
* @author Sebastian Zarnekow - Initial contribution and API
*/
public class GrammarResource extends DerivedStateAwareResource {
/**
* Overridden to do only the clean-part of the linking but not
* the actual linking. This is deferred until someone wants to access
* the content of the resource.
*/
@Override
protected void doLinking() {
IParseResult parseResult = getParseResult();
if (parseResult == null || parseResult.getRootASTElement() == null)
return;
XtextLinker castedLinker = (XtextLinker) getLinker();
castedLinker.discardGeneratedPackages(parseResult.getRootASTElement());
}
/**
* Performs the actual linking.
*/
protected void superDoLinking() {
super.doLinking();
}
/**
* Overridden to make sure the errors are up-to-date when someone wants to access them.
*/
@Override
public EList<Diagnostic> getErrors() {
// trigger derived state computation
getContents();
return super.getErrors();
}
/**
* Overridden to make sure the warnings are up-to-date when someone wants to access them.
*/
@Override
public EList<Diagnostic> getWarnings() {
// trigger derived state computation
getContents();
return super.getWarnings();
}
/**
* Triggers the ecore inference as soon as someone wants to access the contents
* of a {@link GrammarResource}.
*/
public static class LinkingTrigger implements IDerivedStateComputer {
public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) {
if (preLinkingPhase)
return;
GrammarResource castedResource = (GrammarResource)resource;
castedResource.superDoLinking();
}
public void discardDerivedState(DerivedStateAwareResource resource) {
}
}
}

View file

@ -178,9 +178,15 @@ public class XtextLinker extends Linker {
@Override
protected void beforeModelLinked(EObject model, IDiagnosticConsumer diagnosticsConsumer) {
if (model instanceof Grammar) {
discardGeneratedPackages(model);
super.beforeModelLinked(model, diagnosticsConsumer);
cache.getOrCreate(model.eResource()).ignoreNotifications();
}
void discardGeneratedPackages(EObject root) {
if (root instanceof Grammar) {
// unload generated metamodels as they will be recreated during linking
for (AbstractMetamodelDeclaration metamodelDeclaration : ((Grammar) model).getMetamodelDeclarations()) {
for (AbstractMetamodelDeclaration metamodelDeclaration : ((Grammar) root).getMetamodelDeclarations()) {
if (metamodelDeclaration instanceof GeneratedMetamodel) {
EPackage ePackage = ((GeneratedMetamodel) metamodelDeclaration).getEPackage();
if (ePackage != null) {
@ -197,8 +203,6 @@ public class XtextLinker extends Linker {
}
}
}
super.beforeModelLinked(model, diagnosticsConsumer);
cache.getOrCreate(model.eResource()).ignoreNotifications();
}
@Override

View file

@ -41,10 +41,13 @@ import org.eclipse.xtext.nodemodel.BidiIterator;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.parser.IParseResult;
import org.eclipse.xtext.resource.ClasspathUriResolutionException;
import org.eclipse.xtext.resource.ClasspathUriUtil;
import org.eclipse.xtext.resource.DerivedStateAwareResource;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider;
import org.eclipse.xtext.scoping.IGlobalScopeProvider;
import org.eclipse.xtext.scoping.IScope;
@ -95,13 +98,22 @@ public class XtextLinkingService extends DefaultLinkingService {
String grammarName = (String) valueConverterService.toValue("", "GrammarID", node);
if (grammarName != null) {
final ResourceSet resourceSet = grammar.eResource().getResourceSet();
for(Resource resource: resourceSet.getResources()) {
if (!resource.getContents().isEmpty()) {
EObject content = resource.getContents().get(0);
if (content instanceof Grammar) {
Grammar otherGrammar = (Grammar) content;
if (grammarName.equals(otherGrammar.getName()))
return Collections.<EObject>singletonList(otherGrammar);
List<Resource> resources = resourceSet.getResources();
for(int i = 0; i < resources.size(); i++) {
Resource resource = resources.get(i);
EObject rootElement = null;
if (resource instanceof XtextResource) {
IParseResult parseResult = ((XtextResource) resource).getParseResult();
rootElement = parseResult.getRootASTElement();
} else if (!resource.getContents().isEmpty()) {
rootElement = resource.getContents().get(0);
}
if (rootElement instanceof Grammar) {
Grammar otherGrammar = (Grammar) rootElement;
if (grammarName.equals(otherGrammar.getName())) {
if (resource instanceof DerivedStateAwareResource)
resource.getContents();
return Collections.<EObject>singletonList(otherGrammar);
}
}
}

View file

@ -890,6 +890,8 @@ public class XtextValidator extends AbstractDeclarativeValidator {
@Override
public Boolean caseRuleCall(RuleCall object) {
if (object.getRule() == null)
return assignedActionAllowed;
assignedActionAllowed = assignedActionAllowed || doSwitch(object.getRule())
&& !GrammarUtil.isOptionalCardinality(object);
return assignedActionAllowed;

View file

@ -127,6 +127,8 @@ public class EClassifierInfos {
}
public EClassifierInfo getInfoOrNull(EClassifier eClassifier) {
if (eClassifier == null)
return null;
for (EClassifierInfo info : infoMap.values()) {
if (info.getEClassifier().equals(eClassifier))
return info;

View file

@ -40,7 +40,7 @@ public class Xtext2EcoreInterpretationContext {
private final Collection<EClassifierInfo> currentTypes = Sets.newLinkedHashSet();
boolean isRuleCallAllowed = true;
private boolean isRuleCallAllowed = true;
private Xtext2EcoreInterpretationContext(EClassifierInfos classifierInfos) {
super();
@ -128,7 +128,7 @@ public class Xtext2EcoreInterpretationContext {
if (result == null) {
final ICompositeNode node = NodeModelUtils.getNode(terminal);
if (node != null) {
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable, "Cannot find type for '" + node.getText() + "'.", terminal);
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable, "Cannot find type for '" + node.getText().trim() + "'.", terminal);
}
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable, "Cannot find type for " + terminal.eClass().getName(), terminal);
}
@ -139,8 +139,11 @@ public class Xtext2EcoreInterpretationContext {
throws TransformationException {
final EClassifierInfo featureTypeInfo = eClassifierInfos.getInfoOrNull(type);
if (featureTypeInfo == null) {
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable, "Cannot resolve type " + type.getName(),
parserElement);
String typeName = "null";
if (type != null)
typeName = type.getName();
throw new TransformationException(TransformationErrorCode.NoSuchTypeAvailable,
"Cannot resolve type " + typeName, parserElement);
}
return featureTypeInfo;
}

View file

@ -12,7 +12,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -57,6 +56,7 @@ import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.util.XtextSwitch;
import org.eclipse.xtext.xtext.GrammarResource;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
@ -147,15 +147,16 @@ public class Xtext2EcoreTransformer {
public void removeGeneratedPackages() {
final ResourceSet resourceSet = grammar.eResource().getResourceSet();
final Iterator<Resource> resourceIter = resourceSet.getResources().iterator();
final List<Resource> resources = resourceSet.getResources();
final Collection<EPackage> packages = getGeneratedPackages();
// TODO check against grammar
while (resourceIter.hasNext()) {
Resource r = resourceIter.next();
CONTENT: for (EObject content : r.getContents()) {
if (content instanceof EPackage && packages.contains(content) || generatedEPackages != null && generatedEPackages.containsValue(content)) {
clearPackage(r, (EPackage) content);
break CONTENT;
for(int i = 0; i < resources.size(); i++) {
Resource r = resources.get(i);
if (!(r instanceof GrammarResource)) {
CONTENT: for (EObject content : r.getContents()) {
if (content instanceof EPackage && packages.contains(content) || generatedEPackages != null && generatedEPackages.containsValue(content)) {
clearPackage(r, (EPackage) content);
break CONTENT;
}
}
}
}
@ -781,7 +782,6 @@ public class Xtext2EcoreTransformer {
}
else if (eClassifier instanceof EDataType) {
EDataType eDataType = (EDataType) eClassifier;
// TODO: Enums
EClassifierInfo info = EClassifierInfo.createEDataTypeInfo(eDataType, generated);
target.addInfo(metaModel, eClassifier.getName(), info);
}

View file

@ -57,7 +57,7 @@ public class ToEcoreTrafoTest extends AbstractXtextTests {
public void testConcreteLanguageToMetamodel() throws Exception {
XtextResource r = getResource("classpath:/" + ConcreteTestLanguage.class.getName().replace('.', '/') + ".xtext");
Grammar element = (Grammar) r.getParseResult().getRootASTElement();
Grammar element = (Grammar) r.getContents().get(0);
List<TerminalRule> lexerRules = GrammarUtil.allTerminalRules(element);
assertEquals(8, lexerRules.size());
List<EPackage> list = Xtext2EcoreTransformer.doGetGeneratedPackages(element);
@ -74,7 +74,7 @@ public class ToEcoreTrafoTest extends AbstractXtextTests {
public void testConcreteLanguageToMetamodel1() throws Exception {
XtextResource r = getResource("classpath:/" + ConcreteTestLanguage.class.getName().replace('.', '/') + ".xtext");
EObject element = r.getParseResult().getRootASTElement();
EObject element = r.getContents().get(0);
Grammar g = (Grammar) element;
List<AbstractMetamodelDeclaration> mms = Lists.<AbstractMetamodelDeclaration>newArrayList(
Iterables.filter(g.getMetamodelDeclarations(), GeneratedMetamodel.class));