mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 00:38:56 +00:00
cross references first draft
This commit is contained in:
parent
26a41f09e4
commit
930dd4f036
32 changed files with 612 additions and 28 deletions
|
@ -42,5 +42,6 @@
|
|||
<eStructuralFeatures xsi:type="ecore:EAttribute" name="upperBound" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EInt"
|
||||
defaultValueLiteral="1"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EReference" name="type" lowerBound="1" eType="#//AbstractType"/>
|
||||
<eStructuralFeatures xsi:type="ecore:EAttribute" name="containment" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean"/>
|
||||
</eClassifiers>
|
||||
</ecore:EPackage>
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
}
|
|
@ -0,0 +1,9 @@
|
|||
package org.eclipse.xtext.crossref;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
|
||||
public interface ILinker {
|
||||
public List<BrokenLink> ensureLinked(EObject obj);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<EAttribute> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Resource> 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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<BrokenLink> ensureLinked(EObject obj) {
|
||||
List<BrokenLink> brokenLinks = new ArrayList<BrokenLink>();
|
||||
NodeAdapter nodeAdapter = NodeUtil.getNodeAdapter(obj);
|
||||
if (nodeAdapter == null)
|
||||
return brokenLinks;
|
||||
CompositeNode node = nodeAdapter.getParserNode();
|
||||
EList<AbstractNode> 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<BrokenLink> ensureIsLinked(EObject obj, LeafNode node, CrossReference ref) {
|
||||
List<BrokenLink> brokenLinks = new ArrayList<BrokenLink>();
|
||||
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<EObject>) 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<EReference> references = class1.getEAllReferences();
|
||||
String feature = GrammarUtil.containingAssignment(ref).getFeature();
|
||||
for (EReference reference : references) {
|
||||
if (!reference.isContainment() && reference.getName().equals(feature))
|
||||
return reference;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -120,5 +120,5 @@ public class NodeUtil {
|
|||
}
|
||||
System.out.println("} (" + node.getOffset() + ", " + node.getLength() + ")");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -23,7 +23,7 @@ public abstract class AbstractParseTreeConstructor implements IParseTreeConstruc
|
|||
|
||||
@Inject
|
||||
private IGrammarAccess grammar;
|
||||
|
||||
|
||||
public IAstFactory getFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
|
|
@ -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<String, Integer> featureConsumedCounter = new HashMap<String, Integer>();
|
||||
public EObject described;
|
||||
public Map<String, Integer> featureConsumedCounter = new HashMap<String, Integer>();
|
||||
|
||||
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<String, Integer>(featureConsumedCounter));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<BrokenLink> brokenLinks = linker.ensureLinked(parseResult.getRootASTElement());
|
||||
TreeIterator<EObject> 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<String, EObject> map = new HashMap<String, EObject>();
|
||||
Iterator<EObject> 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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package org.eclipse.xtext.crossrefs;
|
||||
|
||||
public class LangA {
|
||||
|
||||
}
|
|
@ -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];
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MetaModel> metamodels = (List<MetaModel>) 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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue