cross references first draft

This commit is contained in:
sefftinge 2008-08-07 15:05:29 +00:00
parent 26a41f09e4
commit 930dd4f036
32 changed files with 612 additions and 28 deletions

View file

@ -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>

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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:

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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); }

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -120,5 +120,5 @@ public class NodeUtil {
}
System.out.println("} (" + node.getOffset() + ", " + node.getLength() + ")");
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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")

View file

@ -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) {
}
}

View file

@ -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();

View file

@ -23,7 +23,7 @@ public abstract class AbstractParseTreeConstructor implements IParseTreeConstruc
@Inject
private IGrammarAccess grammar;
public IAstFactory getFactory() {
return factory;
}

View file

@ -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));
}

View file

@ -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();

View file

@ -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

View file

@ -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();

View file

@ -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);
}
}

View file

@ -0,0 +1,5 @@
package org.eclipse.xtext.crossrefs;
public class LangA {
}

View file

@ -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];

View file

@ -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);
}
}

View file

@ -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);
}
}