mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 08:48:55 +00:00
[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:
parent
280cb4c820
commit
a69606b307
10 changed files with 207 additions and 29 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue