From 930dd4f0365a3c7f518ba03d05332c794c0aa64b Mon Sep 17 00:00:00 2001 From: sefftinge Date: Thu, 7 Aug 2008 15:05:29 +0000 Subject: [PATCH] cross references first draft --- .../org.eclipse.xtext/model/xtextutil.ecore | 1 + .../src/org/eclipse/xtext/GrammarUtil.ext | 3 + .../src/org/eclipse/xtext/GrammarUtil.java | 19 +-- .../src/org/eclipse/xtext/Xtext.xtext | 6 +- .../src/org/eclipse/xtext/XtextUtil.ext | 22 ++-- .../builtin/XtextBuiltinStandaloneSetup.java | 14 +++ .../eclipse/xtext/crossref/BrokenLink.java | 38 ++++++ .../xtext/crossref/IFragmentProvider.java | 29 +++++ .../eclipse/xtext/crossref/ILinkProvider.java | 1 + .../org/eclipse/xtext/crossref/ILinker.java | 9 ++ .../eclipse/xtext/crossref/IURIChecker.java | 25 ++++ .../crossref/impl/DefaultLinkResolver.java | 28 +++++ .../impl/DefaultRuntimeURIChecker.java | 13 ++ .../impl/XtextBuiltinFragmentProvider.java | 24 ++++ .../impl/XtextBuiltinLinkProvider.java | 25 ++++ .../xtext/crossref/internal/Linker.java | 114 ++++++++++++++++++ .../org/eclipse/xtext/parsetree/NodeUtil.java | 2 +- .../reconstr/IInstanceDescription.java | 40 ++++++ .../IParseTreeConstructorCallback.java | 22 +++- .../reconstr/XtextSerializationException.java | 8 ++ ...DefaultParsetreeReconstructorCallback.java | 12 ++ .../callbacks/SimpleSerializingCallback.java | 16 +++ .../impl/AbstractParseTreeConstructor.java | 2 +- .../reconstr/impl/InstanceDescription.java | 18 ++- .../parsetree/reconstr/impl/Predicate.java | 2 +- .../eclipse/xtext/resource/XtextResource.java | 51 ++++++++ .../xtext/GenerateAllTestGrammars.java | 8 +- .../eclipse/xtext/crossrefs/CrossRefTest.java | 19 +++ .../org/eclipse/xtext/crossrefs/LangA.java | 5 + .../org/eclipse/xtext/crossrefs/LangA.xtext | 13 ++ .../eclipse/xtext/parser/XtextParserTest.java | 5 + .../xtext/xtext2ecore/XtextUtilTrafoTest.java | 46 +++++++ 32 files changed, 612 insertions(+), 28 deletions(-) create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/BrokenLink.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IFragmentProvider.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinkProvider.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinker.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IURIChecker.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultLinkResolver.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultRuntimeURIChecker.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinFragmentProvider.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinLinkProvider.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/internal/Linker.java create mode 100644 tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/CrossRefTest.java create mode 100644 tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.java create mode 100644 tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.xtext create mode 100644 tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java diff --git a/plugins/org.eclipse.xtext/model/xtextutil.ecore b/plugins/org.eclipse.xtext/model/xtextutil.ecore index aba2fee1c..8d65711bc 100644 --- a/plugins/org.eclipse.xtext/model/xtextutil.ecore +++ b/plugins/org.eclipse.xtext/model/xtextutil.ecore @@ -42,5 +42,6 @@ + diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext index e44715d14..14bf24fef 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.ext @@ -93,4 +93,7 @@ String getQualifiedName(TypeRef this) : Grammar getSuperGrammar(Grammar this) : JAVA org.eclipse.xtext.GrammarUtil.getSuperGrammar(org.eclipse.xtext.Grammar); + +LexerRule calledLexerRule(CrossReference this) : + JAVA org.eclipse.xtext.GrammarUtil.getCalledLexerRule(org.eclipse.xtext.CrossReference); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java index 757fac0ef..d9239ee02 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java @@ -38,11 +38,11 @@ import org.eclipse.xtext.util.Strings; public class GrammarUtil { private static URI getClasspathURIForLanguageId(String id) { - return URI.createURI("classpath:/" + id.replace('.', '/') + (IXtextBuiltin.ID.equals(id)?".xmi":".xtext")); + return URI.createURI("classpath:/" + id.replace('.', '/') + (IXtextBuiltin.ID.equals(id) ? ".xmi" : ".xtext")); } public static EPackage loadEPackage(ReferencedMetamodel ref) { - if (ref==null) + if (ref == null) throw new NullPointerException("ReferencedMetamodel was null"); if (EPackage.Registry.INSTANCE.containsKey(ref.getUri())) return EPackage.Registry.INSTANCE.getEPackage(ref.getUri()); @@ -150,16 +150,16 @@ public class GrammarUtil { if (_this == null) throw new NullPointerException("Grammar was null"); String id = getSuperGrammarId(_this); - if (id==null) + if (id == null) return null; - if (!(_this.eResource() != null && _this.eResource().getResourceSet() instanceof XtextResourceSet)) + if (!(_this.eResource() != null && _this.eResource().getResourceSet() instanceof XtextResourceSet)) throw new IllegalArgumentException("The passed grammar is not contained in a Resourceset"); ResourceSet resourceSet = _this.eResource().getResourceSet(); URI uri = getClasspathURIForLanguageId(id); - //uri = uri.appendFragment(""); + // uri = uri.appendFragment(""); Resource resource = resourceSet.getResource(uri, true); - if (resource==null) - throw new IllegalArgumentException("Couldn't find grammar for super language "+id); + if (resource == null) + throw new IllegalArgumentException("Couldn't find grammar for super language " + id); Grammar grammar = (Grammar) resource.getContents().get(0); return grammar; } @@ -311,4 +311,9 @@ public class GrammarUtil { return e.getCardinality() != null && (e.getCardinality().equals("*")); } + public static LexerRule getCalledLexerRule(CrossReference ref) { + String ruleName = ref.getRule() != null ? ref.getRule().getName() : "ID"; + return (LexerRule) findRuleForName(getGrammar(ref), ruleName); + } + } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext index f2b8a19e6..079217488 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/Xtext.xtext @@ -63,7 +63,11 @@ Action returns Action: '{' ('current' '=')? typeName=TypeRef '.' feature=ID operator=('='|'+=') 'current' '}';//TODO make assignment optional AbstractTerminal returns AbstractElement: - Keyword | RuleCall | ParenthesizedElement + Keyword | RuleCall | ParenthesizedElement | CrossReference +; + +CrossReference : + '[' type=TypeRef ('|' rule=RuleCall)? ']' ; ParenthesizedElement returns AbstractElement: diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext index bb361e284..37ddd2b68 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/XtextUtil.ext @@ -21,6 +21,11 @@ cached List[MetaModel] getAllMetaModels(Grammar this) : theCurrentType(Action this) : getAllMetaModels(grammar())-> currentType(this,grammar()); + +/* just for testing */ +testCurrentType(RuleCall this) : + getAllMetaModels(grammar())-> + currentType(this,grammar()); private AbstractType typeForName(Grammar this, String name) : getAllMetaModels().select(mm|mm.alias()==name.getAlias()).first().types.select(e|e.name == name.getTypeName()).first(); @@ -59,11 +64,11 @@ private create ComplexType createComplexType(String name, Grammar g) : private setAbstractFeature(Grammar this, List[MetaModel] x) : - x.types.flatten().typeSelect(ComplexType).collect(e|e.setAbstract(e.isAbstract(this))); + x.types.flatten().typeSelect(ComplexType).collect(e|e.setAbstract((e.isAbstract(this)))); private cached Boolean isAbstract(ComplexType name, Grammar g) : g.eAllContents.typeSelect(Action).select(e|e.typeName.getQualifiedName() == name.getQualifiedName()).isEmpty && - g.eAllContents.typeSelect(Assignment).select(e|e.currentType(g).getQualifiedName() == name.getQualifiedName()).isEmpty + g.eAllContents.typeSelect(Assignment).select(e|(e.currentType(g).getQualifiedName()) == (name.getQualifiedName())).isEmpty ; @@ -101,7 +106,8 @@ private Feature update(Feature this, Assignment that, Grammar g) : case '=': setUpperBound(1) default : null } -> - this.setType(returnType(that.terminal,g)) + this.setType(returnType(that.terminal,g)) -> + this.setContainment(that.eAllContents.typeSelect(CrossReference).isEmpty) ) ) -> this @@ -114,7 +120,8 @@ private Feature update(Feature this, Action that, Grammar g) : case '=': setUpperBound(1) default : null } -> - this.setType(currentType(that.eContainer,g)) + this.setType(currentType(that.eContainer,g)) -> + this.setContainment(true) ) -> this ; @@ -123,6 +130,7 @@ private create DataType createDataType(String name, Grammar g) : setName(name.getTypeName()); // returnType private cached AbstractType returnType(Void this, Grammar g) : null; +private cached AbstractType returnType(CrossReference this, Grammar g) : createComplexType(getQualifiedName(this.type),g); private cached AbstractType returnType(AbstractElement this, Grammar g) : error("") -> null; private cached AbstractType returnType(Alternatives this, Grammar g) : groups.returnType(g).toSet().toList().first(); private cached AbstractType returnType(RuleCall this, Grammar g) : @@ -143,9 +151,9 @@ private cached DataType returnType(LexerRule this, Grammar g) : // currentType private cached AbstractType currentType(Void this, Grammar g) : null; -private cached AbstractType currentType(emf::EObject this, Grammar g) : +private AbstractType currentType(emf::EObject this, Grammar g) : if RuleCall.isInstance(this) && !((RuleCall)this).isAssigned() then ( - returnType(calledRule((RuleCall)this),g) + (returnType(calledRule((RuleCall)this),g)) ) else ( let containmentList = eContainer.eContents: let index = containmentList.indexOf(this) : @@ -172,7 +180,7 @@ private cached ComplexType currentType(Action this, Grammar g) : this.typeName.getQualifiedName().createComplexType(g); private cached ComplexType currentType(ParserRule this, Grammar g) : - this.getReturnTypeName().createComplexType(g); + getReturnTypeName().createComplexType(g); private cached DataType currentType(LexerRule this, Grammar g) : this.getReturnTypeName().createDataType(g); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltinStandaloneSetup.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltinStandaloneSetup.java index f6003d1af..56c985073 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltinStandaloneSetup.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/builtin/XtextBuiltinStandaloneSetup.java @@ -7,6 +7,14 @@ import org.eclipse.xtext.IGrammarAccess; import org.eclipse.xtext.XtextPackage; import org.eclipse.xtext.builtin.conversion.XtextBuiltInConverters; import org.eclipse.xtext.conversion.IValueConverterService; +import org.eclipse.xtext.crossref.IFragmentProvider; +import org.eclipse.xtext.crossref.ILinkProvider; +import org.eclipse.xtext.crossref.ILinker; +import org.eclipse.xtext.crossref.IURIChecker; +import org.eclipse.xtext.crossref.impl.DefaultRuntimeURIChecker; +import org.eclipse.xtext.crossref.impl.XtextBuiltinFragmentProvider; +import org.eclipse.xtext.crossref.impl.XtextBuiltinLinkProvider; +import org.eclipse.xtext.crossref.internal.Linker; import org.eclipse.xtext.service.IServiceScope; import org.eclipse.xtext.service.ServiceRegistry; import org.eclipse.xtext.service.ServiceScopeFactory; @@ -25,6 +33,12 @@ public class XtextBuiltinStandaloneSetup { ServiceRegistry.registerService(serviceScope, IGrammarAccess.class, XtextBuiltinGrammarAccess.class); ServiceRegistry.registerService(serviceScope, IValueConverterService.class, XtextBuiltInConverters.class); + ServiceRegistry.registerService(serviceScope, ILinker.class, Linker.class); + ServiceRegistry.registerService(serviceScope, ILinkProvider.class, XtextBuiltinLinkProvider.class); + ServiceRegistry.registerService(serviceScope, IURIChecker.class, DefaultRuntimeURIChecker.class); + ServiceRegistry.registerService(serviceScope, IFragmentProvider.class, XtextBuiltinFragmentProvider.class); + + isInitialized = true; } } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/BrokenLink.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/BrokenLink.java new file mode 100644 index 000000000..62d07bc2d --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/BrokenLink.java @@ -0,0 +1,38 @@ +package org.eclipse.xtext.crossref; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.parsetree.LeafNode; + +public class BrokenLink { + private EObject from; + private LeafNode linkInformation; + private CrossReference grammarElement; + private URI resolvedURI; + + public BrokenLink(EObject from, LeafNode linkInformation, CrossReference grammarElement, URI resolvedURI) { + super(); + this.from = from; + this.linkInformation = linkInformation; + this.grammarElement = grammarElement; + this.resolvedURI = resolvedURI; + } + + public EObject getFrom() { + return from; + } + + public LeafNode getLinkInformation() { + return linkInformation; + } + + public CrossReference getGrammarElement() { + return grammarElement; + } + + public URI getResolvedURI() { + return resolvedURI; + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IFragmentProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IFragmentProvider.java new file mode 100644 index 000000000..31b433d02 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IFragmentProvider.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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.crossref; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.service.ILanguageService; + +/** + * Used to compute fragments (i.e. resource local IDs) for EObjects. Typically + * used within a Resource + * + * @author Sven Efftinge - Initial contribution and API + * + */ +public interface IFragmentProvider extends ILanguageService { + /** + * @param obj + * The EObject to compute the fragment for + * @return the fragment, which can be an arbitrary string but must be unique + * within a resource + */ + String getFragment(EObject obj); +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinkProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinkProvider.java new file mode 100644 index 000000000..46644fd02 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinkProvider.java @@ -0,0 +1 @@ +/******************************************************************************* * 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.crossref; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.parsetree.LeafNode; import org.eclipse.xtext.service.ILanguageService; /** * @author Sven Efftinge - Initial contribution and API * */ public interface ILinkProvider extends ILanguageService { /** * is called to compute the URI of the EObject referenced by * the given text in the given model. */ URI[] getLinks(LeafNode text, CrossReference ref, EObject model); } \ No newline at end of file diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinker.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinker.java new file mode 100644 index 000000000..6c00a79a9 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/ILinker.java @@ -0,0 +1,9 @@ +package org.eclipse.xtext.crossref; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; + +public interface ILinker { + public List ensureLinked(EObject obj); +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IURIChecker.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IURIChecker.java new file mode 100644 index 000000000..3364b44c6 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/IURIChecker.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * 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.crossref; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; + +/** + * @author Sven Efftinge - Initial contribution and API + * + */ +public interface IURIChecker { + /** + * @param uri + * @param context + * @return + */ + boolean exists(URI uri, EObject context); +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultLinkResolver.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultLinkResolver.java new file mode 100644 index 000000000..725b6dd8f --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultLinkResolver.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.crossref.impl; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.crossref.ILinkProvider; +import org.eclipse.xtext.parsetree.LeafNode; + +/** + * @author Sven Efftinge - Initial contribution and API + * + */ +public class DefaultLinkResolver implements ILinkProvider { + + public URI[] getLinks(LeafNode text, CrossReference ref, EObject model) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultRuntimeURIChecker.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultRuntimeURIChecker.java new file mode 100644 index 000000000..c67be5491 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/DefaultRuntimeURIChecker.java @@ -0,0 +1,13 @@ +package org.eclipse.xtext.crossref.impl; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.crossref.IURIChecker; + +public class DefaultRuntimeURIChecker implements IURIChecker { + + public boolean exists(URI uri, EObject context) { + return context.eResource().getResourceSet().getEObject(uri, true)!=null; + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinFragmentProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinFragmentProvider.java new file mode 100644 index 000000000..9d77f875f --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinFragmentProvider.java @@ -0,0 +1,24 @@ +package org.eclipse.xtext.crossref.impl; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.crossref.IFragmentProvider; + +public class XtextBuiltinFragmentProvider implements IFragmentProvider { + + public String getFragment(EObject obj) { + EClass eclass = obj.eClass(); + EList attributes = eclass.getEAllAttributes(); + for (EAttribute a : attributes) { + if (a.getName().equals("name")) { + Object name = obj.eGet(a); + if (name instanceof String) + return (String) name; + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinLinkProvider.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinLinkProvider.java new file mode 100644 index 000000000..9dd3c7214 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/impl/XtextBuiltinLinkProvider.java @@ -0,0 +1,25 @@ +package org.eclipse.xtext.crossref.impl; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.crossref.ILinkProvider; +import org.eclipse.xtext.parsetree.LeafNode; + +public class XtextBuiltinLinkProvider implements ILinkProvider { + + public URI[] getLinks(LeafNode text, CrossReference ref, EObject model) { + ResourceSet resourceSet = model.eResource().getResourceSet(); + EList resources = resourceSet.getResources(); + for (Resource resource : resources) { + EObject object = resource.getEObject(text.getText()); + if (object!=null) + return new URI[]{resource.getURI().appendFragment(text.getText())}; + } + return null; + } + +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/internal/Linker.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/internal/Linker.java new file mode 100644 index 000000000..93a167e49 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/crossref/internal/Linker.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * 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.crossref.internal; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.xtext.CrossReference; +import org.eclipse.xtext.GrammarUtil; +import org.eclipse.xtext.crossref.BrokenLink; +import org.eclipse.xtext.crossref.ILinkProvider; +import org.eclipse.xtext.crossref.ILinker; +import org.eclipse.xtext.crossref.IURIChecker; +import org.eclipse.xtext.parser.IAstFactory; +import org.eclipse.xtext.parsetree.AbstractNode; +import org.eclipse.xtext.parsetree.CompositeNode; +import org.eclipse.xtext.parsetree.LeafNode; +import org.eclipse.xtext.parsetree.NodeAdapter; +import org.eclipse.xtext.parsetree.NodeUtil; +import org.eclipse.xtext.service.Inject; + +public final class Linker implements ILinker { + + @Inject + private ILinkProvider linker; + + @Inject + private IAstFactory factory; + + @Inject + private IURIChecker checker; + + public List ensureLinked(EObject obj) { + List brokenLinks = new ArrayList(); + NodeAdapter nodeAdapter = NodeUtil.getNodeAdapter(obj); + if (nodeAdapter == null) + return brokenLinks; + CompositeNode node = nodeAdapter.getParserNode(); + EList children = node.getChildren(); + for (AbstractNode abstractNode : children) { + if (abstractNode instanceof LeafNode && abstractNode.getGrammarElement() instanceof CrossReference) { + CrossReference ref = (CrossReference) abstractNode.getGrammarElement(); + brokenLinks.addAll(ensureIsLinked(obj, (LeafNode) abstractNode, ref)); + } + } + return brokenLinks; + } + + @SuppressWarnings("unchecked") + private List ensureIsLinked(EObject obj, LeafNode node, CrossReference ref) { + List brokenLinks = new ArrayList(); + URI[] links = linker.getLinks((LeafNode) node, ref, obj); + if (links==null || links.length == 0) { + brokenLinks.add(new BrokenLink(obj, node, ref, null)); + return brokenLinks; + } + EReference eRef = getReference(ref, obj.eClass()); + if (eRef == null) + throw new IllegalStateException("Couldn't find EReference for cross reference " + ref); + if (eRef.getUpperBound() == 1) { + if (links.length > 1) + throw new IllegalStateException("The feature " + eRef.getName() + " cannot hold multiple values : " + + Arrays.asList(links)); + + URI uri = links[0]; + if (checker.exists(uri,obj)) { + EObject proxy = createProxy(ref, uri); + obj.eSet(eRef, proxy); + } else { + brokenLinks.add(new BrokenLink(obj, node, ref, uri)); + } + } else { // eRef.getUpperBound() == -1 + for (URI uri : links) { + if (checker.exists(uri,obj)) { + EObject proxy = createProxy(ref, uri); + ((EList) obj.eGet(eRef)).add(proxy); + } else { + brokenLinks.add(new BrokenLink(obj, node, ref, uri)); + } + } + } + return brokenLinks; + } + + private EObject createProxy(CrossReference ref, URI uri) { + EObject proxy = factory.create(GrammarUtil.getQualifiedName(ref.getType())); + ((InternalEObject) proxy).eSetProxyURI(uri); + return proxy; + } + + private EReference getReference(CrossReference ref, EClass class1) { + EList references = class1.getEAllReferences(); + String feature = GrammarUtil.containingAssignment(ref).getFeature(); + for (EReference reference : references) { + if (!reference.isContainment() && reference.getName().equals(feature)) + return reference; + } + return null; + } +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java index b4ee4a0b4..49f0c7a87 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/NodeUtil.java @@ -120,5 +120,5 @@ public class NodeUtil { } System.out.println("} (" + node.getOffset() + ", " + node.getLength() + ")"); } - + } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java index bb9182893..48d4c7ecb 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IInstanceDescription.java @@ -2,20 +2,60 @@ package org.eclipse.xtext.parsetree.reconstr; import org.eclipse.emf.ecore.EObject; +/** + * A wrapper for EObjects holding the information about what values have already + * been consumed by a serialization process. + * + * @author Sven Efftinge - Initial contribution and API + */ public interface IInstanceDescription { + /** + * @param the + * type name as it is used within the grammar of the given + * language + * @return true if the delegate is a direct instance of the given type + */ public abstract boolean isInstanceOf(String string); + /** + * @param the + * type name as it is used within the grammar of the given + * language + * @return true if the delegate's type is assignable to the given type + */ public abstract boolean isOfType(String string); + /** + * @return the wrapped EObject + */ public abstract EObject getDelegate(); + /** + * returns the last consumed value in the given feature + * + * @param feature + * @return the consumed value + */ public abstract Object get(String feature); + /** + * + * @param feature + * @return whether there are any consumable values for the given feature + */ public abstract boolean isConsumable(String feature); + /** + * + * @return whether all values referenced by the delegate have been consumed + */ public abstract boolean isConsumed(); + /** + * @param feature + * @return the number of values already consumed for this feature + */ public abstract int getConsumed(String feature); } \ No newline at end of file diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java index b56b06a75..d5db92bc4 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/IParseTreeConstructorCallback.java @@ -1,14 +1,32 @@ +/******************************************************************************* + * 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.parsetree.reconstr; import org.eclipse.xtext.Action; +import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.service.ILanguageService; + +public interface IParseTreeConstructorCallback extends ILanguageService { -public interface IParseTreeConstructorCallback { void parserRuleCallStart(IInstanceDescription current, RuleCall call); + void parserRuleCallEnd(); + void objectCreation(IInstanceDescription current); + void lexerRuleCall(IInstanceDescription current, RuleCall call); + void keywordCall(IInstanceDescription current, Keyword call); - void actionCall(IInstanceDescription oldCurrent,IInstanceDescription newCurrent, Action action); + + void actionCall(IInstanceDescription oldCurrent, IInstanceDescription newCurrent, Action action); + + void crossRefCall(IInstanceDescription current, CrossReference call); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/XtextSerializationException.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/XtextSerializationException.java index 6d935c928..cd45adb07 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/XtextSerializationException.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/XtextSerializationException.java @@ -1,3 +1,11 @@ +/******************************************************************************* + * 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.parsetree.reconstr; @SuppressWarnings("serial") diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java index 8f1293584..f1755eb5e 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/DefaultParsetreeReconstructorCallback.java @@ -1,6 +1,15 @@ +/******************************************************************************* + * 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.parsetree.reconstr.callbacks; import org.eclipse.xtext.Action; +import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.parsetree.reconstr.IInstanceDescription; @@ -26,4 +35,7 @@ public class DefaultParsetreeReconstructorCallback implements IParseTreeConstruc public void parserRuleCallStart(IInstanceDescription current, RuleCall call) { } + public void crossRefCall(IInstanceDescription current, CrossReference call) { + } + } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java index cf3d8c0e5..0d849ceb5 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/callbacks/SimpleSerializingCallback.java @@ -1,7 +1,9 @@ package org.eclipse.xtext.parsetree.reconstr.callbacks; +import org.eclipse.emf.ecore.EObject; import org.eclipse.xtext.AbstractElement; import org.eclipse.xtext.Assignment; +import org.eclipse.xtext.CrossReference; import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; @@ -9,6 +11,7 @@ import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.parsetree.reconstr.IInstanceDescription; public class SimpleSerializingCallback extends DefaultParsetreeReconstructorCallback { + private StringBuffer buff = new StringBuffer(); private IValueConverterService converterService; @@ -47,6 +50,19 @@ public class SimpleSerializingCallback extends DefaultParsetreeReconstructorCall before(current, call); } + @Override + public void crossRefCall(IInstanceDescription current, CrossReference call) { + Assignment ass = GrammarUtil.containingAssignment(call); + if (ass==null) + throw new IllegalStateException("Unassigned cross reference "+call); + Object object = current.get(ass.getFeature()); + if (object instanceof EObject) { + EObject obj = (EObject) object; + prepend(obj.eResource().getURIFragment(obj)); + } + throw new IllegalStateException("Can't serialize cross reference to "+object); + } + @Override public String toString() { return buff.toString(); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java index 8037f7d7b..a9f8ee78f 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/AbstractParseTreeConstructor.java @@ -23,7 +23,7 @@ public abstract class AbstractParseTreeConstructor implements IParseTreeConstruc @Inject private IGrammarAccess grammar; - + public IAstFactory getFactory() { return factory; } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java index 8d19774ff..b49dcc697 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/InstanceDescription.java @@ -26,7 +26,7 @@ public class InstanceDescription implements IInstanceDescription { /** * */ - private AbstractParseTreeConstructor parseTreeConstr; + public AbstractParseTreeConstructor parseTreeConstr; /* (non-Javadoc) * @see org.eclipse.xtext.parsetree.impl.IInstanceDescription#isInstanceOf(java.lang.String) @@ -51,8 +51,8 @@ public class InstanceDescription implements IInstanceDescription { return described; } - private EObject described; - private Map featureConsumedCounter = new HashMap(); + public EObject described; + public Map featureConsumedCounter = new HashMap(); public InstanceDescription(AbstractParseTreeConstructor abstractInternalParseTreeConstructor, EObject described) { super(); @@ -87,6 +87,16 @@ public class InstanceDescription implements IInstanceDescription { return hashCode() + "/" + described.hashCode(); } + /** + * consumes a value in the given feature and marks it as consumed. For + * multiple values (i.e. EStructuralFeature.isMultiple()==true) the values + * are consumed reverse starting with the last value in the list. + * + * @param feature + * @return the consumed value + * @throws IllegalStateException + * if the feature is not consumable + */ public Object consume(String feature) { if (!isConsumable(feature)) throw new IllegalStateException(feature + " is not consumable"); @@ -159,7 +169,7 @@ public class InstanceDescription implements IInstanceDescription { return true; } - public InstanceDescription clone() { + public IInstanceDescription createClone() { return new InstanceDescription(this.parseTreeConstr, described, new HashMap(featureConsumedCounter)); } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/Predicate.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/Predicate.java index 6d5be5b11..a89fd7931 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/Predicate.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parsetree/reconstr/impl/Predicate.java @@ -19,7 +19,7 @@ public abstract class Predicate { protected InstanceDescription obj; public Predicate(InstanceDescription obj) { - this.obj = obj.clone(); + this.obj = (InstanceDescription) obj.createClone(); } public abstract boolean check(); diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java index 16cc2be7a..1a8a3fca2 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/XtextResource.java @@ -11,13 +11,23 @@ package org.eclipse.xtext.resource; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.eclipse.emf.common.notify.Adapter; 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.ecore.EObject; import org.eclipse.emf.ecore.resource.impl.ResourceImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.crossref.BrokenLink; +import org.eclipse.xtext.crossref.IFragmentProvider; +import org.eclipse.xtext.crossref.ILinker; import org.eclipse.xtext.parser.IAstFactory; import org.eclipse.xtext.parser.IParseResult; import org.eclipse.xtext.parser.IParser; @@ -39,6 +49,14 @@ public class XtextResource extends ResourceImpl { @Inject private IAstFactory elementFactory; + @Inject + private ILinker linker; + + @Inject + private IFragmentProvider fragmentProvider; + + private Log log = LogFactory.getLog(getClass()); + private IParseResult parseResult; public XtextResource(URI uri) { @@ -69,6 +87,21 @@ public class XtextResource extends ResourceImpl { addNodeContentAdapter(); } } + doLinking(); + } + + protected void doLinking() { + if (parseResult.getRootASTElement() == null) + return; + indexIds(); + List brokenLinks = linker.ensureLinked(parseResult.getRootASTElement()); + TreeIterator allContents = parseResult.getRootASTElement().eAllContents(); + while (allContents.hasNext()) + brokenLinks.addAll(linker.ensureLinked(allContents.next())); + + // for (BrokenLink brokenLink : brokenLinks) { + // //TODO handle broken links + // } } private void addNodeContentAdapter() { @@ -86,6 +119,24 @@ public class XtextResource extends ResourceImpl { } addNodeContentAdapter(); } + doLinking(); + } + + private void indexIds() { + Map map = new HashMap(); + Iterator iter = EcoreUtil.getAllContents(parseResult.getRootASTElement(), false); + while (iter.hasNext()) { + EObject object = (EObject) iter.next(); + String fragment = fragmentProvider.getFragment(object); + if (fragment != null) { + if (map.put(fragment, object) != null) { + map.remove(fragment); + log.info("The id " + fragment + + " is not unique within the resource. Will use relative pathes instead."); + } + } + } + setIntrinsicIDToEObjectMap(map); } @Override diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/GenerateAllTestGrammars.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/GenerateAllTestGrammars.java index ec76780e6..e0759f0a0 100755 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/GenerateAllTestGrammars.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/GenerateAllTestGrammars.java @@ -12,6 +12,7 @@ import org.apache.log4j.Logger; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.xtext.crossrefs.LangA; import org.eclipse.xtext.dummy.DummyLanguage; import org.eclipse.xtext.grammarinheritance.AbstractTestLanguage; import org.eclipse.xtext.grammarinheritance.ConcreteTestLanguage; @@ -36,12 +37,11 @@ public class GenerateAllTestGrammars { private static Logger log = Logger.getLogger(GenerateAllTestGrammars.class); - public final static Class[] testclasses = new Class[] { AbstractTestLanguage.class,ConcreteTestLanguage.class,XtextGrammarTest.class, MetamodelRefTest.class, + public final static Class[] testclasses = new Class[] { LangA.class, AbstractTestLanguage.class,ConcreteTestLanguage.class,XtextGrammarTest.class, MetamodelRefTest.class, DummyLanguage.class, TestLanguage.class, SimpleReconstrTest.class, ComplexReconstrTest.class, LexerLanguage.class, SimpleExpressions.class, ActionTestLanguage.class, OptionalEmptyLanguage.class, - ReferenceGrammar.class, LookaheadLanguage.class - };// MultiGenMMTest. - // class + ReferenceGrammar.class, LookaheadLanguage.class + }; public static void main(String[] args) throws Exception { XtextStandaloneSetup.doSetup(); diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/CrossRefTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/CrossRefTest.java new file mode 100644 index 000000000..d7274ecbd --- /dev/null +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/CrossRefTest.java @@ -0,0 +1,19 @@ +package org.eclipse.xtext.crossrefs; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.tests.AbstractGeneratorTest; + +public class CrossRefTest extends AbstractGeneratorTest { + @Override + protected void setUp() throws Exception { + super.setUp(); + with(LangAStandaloneSetup.class); + } + + public void testSimple() throws Exception { + EObject model = getModel("type A extends B type B extends A"); + System.out.println(invokeWithXtend("types.collect(e|e.name+' '+e.extends.name).toString(',')", model)); + assertWithXtend("'B'", "types.first().extends.name", model); + assertWithXtend("types.first()", "types.first().extends.extends", model); + } +} diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.java new file mode 100644 index 000000000..6d0a0c778 --- /dev/null +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.java @@ -0,0 +1,5 @@ +package org.eclipse.xtext.crossrefs; + +public class LangA { + +} diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.xtext b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.xtext new file mode 100644 index 000000000..d0c6c019e --- /dev/null +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/crossrefs/LangA.xtext @@ -0,0 +1,13 @@ +language org.eclipse.xtext.crossrefs.LangA + +generate langA "http://eclipse.org/xtext/langA" + +Main : + imports+=Import* + types+=Type*; + +Import : + 'import' uri=STRING; + +Type : + 'type' name=ID 'extends' ^extends=[Type]; \ No newline at end of file diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parser/XtextParserTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parser/XtextParserTest.java index 6ed59dd6f..e66ba1191 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parser/XtextParserTest.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/parser/XtextParserTest.java @@ -15,4 +15,9 @@ public class XtextParserTest extends AbstractGeneratorTest { Grammar model = (Grammar) getModel("language foo generate foo 'bar' as x Model returns x::Foo : 'holla' name=ID;"); assertWithXtend("'x'", "metamodelDeclarations.first().alias", model); } + + public void testParseCrossRef() throws Exception { + Grammar model = (Grammar) getModel("language foo generate foo 'bar' Model : 'a' stuff+=Stuff*; Stuff : 'stuff' name=ID refersTo=[Stuff];"); + assertWithXtend("'Stuff'", "eAllContents.typeSelect(xtext::CrossReference).first().type.name", model); + } } diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java new file mode 100644 index 000000000..2c00407a3 --- /dev/null +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/xtext2ecore/XtextUtilTrafoTest.java @@ -0,0 +1,46 @@ +package org.eclipse.xtext.xtext2ecore; + +import java.util.List; + +import org.eclipse.xtext.Grammar; +import org.eclipse.xtext.RuleCall; +import org.eclipse.xtext.XtextStandaloneSetup; +import org.eclipse.xtext.tests.AbstractGeneratorTest; +import org.eclipse.xtext.xtextutil.MetaModel; +import org.eclipse.xtext.xtextutil.XtextutilPackage; + +public class XtextUtilTrafoTest extends AbstractGeneratorTest { + @Override + protected void setUp() throws Exception { + super.setUp(); + XtextutilPackage.eINSTANCE.getAbstractType(); // initialze and register + with(XtextStandaloneSetup.class); + } + + @SuppressWarnings("unchecked") + public void testCrossRefs() throws Exception { + Grammar model = (Grammar) getModel("language foo generate foo 'bar' Model : stuff+=Stuff*; Stuff : 'stuff' name=ID refersTo=[Stuff];"); + List metamodels = (List) invokeWithXtend("getAllMetaModels(this)", model); + assertEquals(2, metamodels.size()); + assertWithXtend("'Stuff'", + "eAllContents.flatten().typeSelect(xtextutil::ComplexType).select(e|e.name=='Stuff')" + + ".first().features.selectFirst(e|e.name=='refersTo').type.name", metamodels); + } + + @Override + protected String[] importedExtensions() { + return new String[] { "org::eclipse::xtext::XtextUtil" }; + } + + public void testCurrentType() throws Exception { + Grammar model = (Grammar) getModel("language org.eclipse.xtext.crossrefs.LangA" + + " generate langA 'http://eclipse.org/xtext/langA' " + + "Main : ^import+=Import* types+=Type*;" + + "Import : 'import' uri=STRING;" + + "Type : 'type' name=ID 'extends' ^extends=[Type];"); + RuleCall rc = (RuleCall) invokeWithXtend("parserRules.first().eAllContents.typeSelect(xtext::RuleCall).first()", model); + assertNotNull(rc); + assertWithXtend("'Main'", "testCurrentType().name", rc); + assertWithXtend("false", "testCurrentType().abstract", rc); + } +}