Patch from Heiko Behrens (Knut) BUG 247406 [Core] Reimplement derivation of metamodel from Xtext grammar in Java

This commit is contained in:
dhubner 2008-09-24 14:27:39 +00:00
parent c59f92fa20
commit 8f5471e0e1
4 changed files with 228 additions and 63 deletions

View file

@ -9,7 +9,6 @@
package org.eclipse.xtext.resource.metamodel;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
@ -17,7 +16,8 @@ import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcoreFactory;
/**
* @author Jan K?hnlein - Initial contribution and API
* @author Jan Köhnlein - Initial contribution and API
* @author Heiko Behrens
*
*/
public abstract class EClassifierInfo {
@ -39,7 +39,7 @@ public abstract class EClassifierInfo {
return new EDataTypeInfo(eDataType, isGenerated);
}
public EClassifier getEClass() {
public EClassifier getEClassifier() {
return eClassifier;
}
@ -65,24 +65,28 @@ public abstract class EClassifierInfo {
if (!(superTypeInfo instanceof EClassInfo)) {
throw new IllegalArgumentException("superTypeInfo must represent EClass");
}
EClass eClass = (EClass) getEClass();
EClass superEClass = (EClass) superTypeInfo.getEClass();
EClass eClass = (EClass) getEClassifier();
EClass superEClass = (EClass) superTypeInfo.getEClassifier();
return eClass.getESuperTypes().add(superEClass);
}
@Override
public boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue) {
EClassifier featureClassifier = featureType.getEClass();
EClassifier featureClassifier = featureType.getEClassifier();
EStructuralFeature newFeature;
EAttribute attribute = EcoreFactory.eINSTANCE.createEAttribute();
attribute.setName(featureName);
attribute.setEType(featureClassifier);
attribute.setLowerBound(0);
attribute.setUpperBound(isMultivalue ? -1 : 1);
if (featureClassifier instanceof EClass)
newFeature = EcoreFactory.eINSTANCE.createEReference();
else
newFeature = EcoreFactory.eINSTANCE.createEAttribute();
newFeature.setName(featureName);
newFeature.setEType(featureClassifier);
newFeature.setLowerBound(0);
newFeature.setUpperBound(isMultivalue ? -1 : 1);
EList<EStructuralFeature> features = ((EClass) getEClass()).getEStructuralFeatures();
return features.add(attribute);
EList<EStructuralFeature> features = ((EClass) getEClassifier()).getEStructuralFeatures();
return features.add(newFeature);
}
}

View file

@ -8,20 +8,23 @@
*******************************************************************************/
package org.eclipse.xtext.resource.metamodel;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.TypeRef;
import org.eclipse.xtext.util.Pair;
import org.eclipse.xtext.util.Strings;
/**
* A possible extension would be to normalize the type hierarchy and remove
* redundant supertype references. We currently don't think thats necessary as
* EMF handles multiple inheritance gracefully.
*
* @author Jan K?hnlein - Initial contribution and API
* @author Jan Köhnlein - Initial contribution and API
*
*/
public class EClassifierInfos {
@ -47,5 +50,45 @@ public class EClassifierInfos {
public void addAll(EClassifierInfos classInfos) {
infoMap.putAll(classInfos.infoMap);
}
private String getCompatibleTypeNameOf(String typeA, String typeB) {
EClassifier classifierA = getInfo(typeA).getEClassifier();
EClassifier classifierB = getInfo(typeB).getEClassifier();
if (classifierA.equals(classifierB))
return typeA;
if (classifierA instanceof EDataType || classifierB instanceof EDataType)
throw new IllegalArgumentException(
"Simple Datatypes (lexer rules or keywords) do not have a common supertype (" + typeA + ", "
+ typeB + ")");
// TODO EClass commonSupertype = EcoreUtil2.getCommonCompatibleType((EClass) classifierA, (EClass) classifierB);
EClass commonSupertype = classifierA.equals(classifierB) ? (EClass)classifierA : null;
if(commonSupertype != null)
return getQualifiedNameFor(commonSupertype);
else
return "ecore::EObject";
}
private String getQualifiedNameFor(EClass eClass) {
// lookup could be improved
for (String key : infoMap.keySet()) {
EClassifierInfo info = infoMap.get(key);
if (info.getEClassifier().equals(eClass))
return key;
}
return null;
}
public String getCompatibleTypeNameOf(Collection<String> typeNames) {
Iterator<String> i = typeNames.iterator();
if (!i.hasNext())
throw new IllegalArgumentException("Empty set of types cannot have a common super type.");
String result = i.next();
while (i.hasNext())
result = getCompatibleTypeNameOf(result, i.next());
return result;
}
}

View file

@ -15,13 +15,17 @@ import java.util.Set;
import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Alternatives;
import org.eclipse.xtext.Assignment;
import org.eclipse.xtext.CrossReference;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Group;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
/**
* @author Heiko Behrens - Initial contribution and API
*
* @see http://wiki.eclipse.org/Xtext/Documentation#Meta-Model_Inference
*/
public class Xtext2ECoreInterpretationContext {
private EClassifierInfos eClassifierInfos;
@ -62,17 +66,57 @@ public class Xtext2ECoreInterpretationContext {
isMultivalue = false;
}
else {
RuleCall featureRuleCall = (RuleCall) assignment.getTerminal();
AbstractRule featureTypeRule = GrammarUtil.calledRule(featureRuleCall);
String featureTypeName = GrammarUtil.getReturnTypeName(featureTypeRule);
String featureTypeName = getTerminalTypeName(assignment.getTerminal());
featureTypeInfo = getEClassifierInfoOrThrowException(featureTypeName, assignment);
}
for (EClassifierInfo type : currentTypes)
type.addFeature(featureName, featureTypeInfo, isMultivalue);
}
private String getTerminalTypeName(AbstractElement terminal) {
if (terminal instanceof RuleCall) {
RuleCall featureRuleCall = (RuleCall) terminal;
AbstractRule featureTypeRule = GrammarUtil.calledRule(featureRuleCall);
return GrammarUtil.getReturnTypeName(featureTypeRule);
}
else if (terminal instanceof CrossReference) {
CrossReference crossReference = (CrossReference) terminal;
return GrammarUtil.getQualifiedName(crossReference.getType());
}
else {
// terminal is ParenthesizedElement
// must be either: alternative of lexer rules and keywords or
// alternative of parser rules
return getCompatibleTypeNameOfAlternatives((Alternatives) terminal);
}
}
private String getCompatibleTypeNameOfAlternatives(AbstractElement element) {
if (element instanceof Alternatives) {
Alternatives alternative = (Alternatives) element;
Set<String> typeNames = new HashSet<String>();
for (AbstractElement group : alternative.getGroups())
typeNames.add(getCompatibleTypeNameOfAlternatives(group));
return eClassifierInfos.getCompatibleTypeNameOf(typeNames);
}
else if (element instanceof Group) {
Group group = (Group) element;
if (group.getAbstractTokens().size() != 1)
throw new IllegalArgumentException("Group must have exactly one element.");
return getCompatibleTypeNameOfAlternatives(group.getAbstractTokens().get(0));
}
else if (element instanceof RuleCall) {
AbstractRule calledRule = GrammarUtil.calledRule((RuleCall) element);
return GrammarUtil.getReturnTypeName(calledRule);
}
else if (element instanceof Keyword) {
return "ecore::EString";
}
else
throw new IllegalArgumentException("Terminal has no type");
}
private EClassifierInfo getEClassifierInfoOrThrowException(String typeName, AbstractElement parserElement)
throws TransformationException {
EClassifierInfo featureTypeInfo = eClassifierInfos.getInfo(typeName);

View file

@ -7,23 +7,22 @@
*******************************************************************************/
package org.eclipse.xtext.resource.metamodel;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.impl.AESCipherImpl;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.XtextStandaloneSetup;
import org.eclipse.xtext.tests.AbstractGeneratorTest;
/**
* @author Jan Köhnlein - Initial contribution and API
* @author Heiko Behrens
* @see http://wiki.eclipse.org/Xtext/Documentation#Meta-Model_Inference
*/
public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
@ -44,8 +43,8 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
assertNotNull(result);
return result;
}
private EAttribute assertFeatureConfiguration(EClass eClass, int attributeIndex, String featureName,
private EAttribute assertAttributeConfiguration(EClass eClass, int attributeIndex, String featureName,
String featureTypeName) {
EAttribute feature = eClass.getEAttributes().get(attributeIndex);
assertEquals(featureName, feature.getName());
@ -55,14 +54,26 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
return feature;
}
private EAttribute assertFeatureConfiguration(EClass eClass, int attributeIndex, String featureName,
private EAttribute assertAttributeConfiguration(EClass eClass, int attributeIndex, String featureName,
String featureTypeName, int lowerBound, int upperBound) {
EAttribute feature = assertFeatureConfiguration(eClass, attributeIndex, featureName, featureTypeName);
EAttribute feature = assertAttributeConfiguration(eClass, attributeIndex, featureName, featureTypeName);
assertEquals(lowerBound, feature.getLowerBound());
assertEquals(upperBound, feature.getUpperBound());
return feature;
}
private EReference assertReferenceConfiguration(EClass eClass, int referenceIndex, String featureName,
String featureTypeName, int lowerBound, int upperBound) {
EReference reference = eClass.getEReferences().get(referenceIndex);
assertEquals(featureName, reference.getName());
assertNotNull(reference.getEType());
assertEquals(featureTypeName, reference.getEType().getName());
assertEquals(lowerBound, reference.getLowerBound());
assertEquals(upperBound, reference.getUpperBound());
return reference;
}
public void testTypesOfImplicitSuperGrammar() throws Exception {
final String xtextGrammar = "language test generate test 'http://test' MyRule: myFeature=INT;";
@ -114,7 +125,7 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
assertNotNull(ruleA);
assertEquals(1, ruleA.getEAttributes().size());
assertFeatureConfiguration(ruleA, 0, "featureA", "EInt");
assertAttributeConfiguration(ruleA, 0, "featureA", "EInt");
}
public void testBuiltInFeatureTypes() throws Exception {
@ -124,9 +135,9 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
assertNotNull(ruleA);
assertEquals(3, ruleA.getEAttributes().size());
assertFeatureConfiguration(ruleA, 0, "featureA", "EString");
assertFeatureConfiguration(ruleA, 1, "featureB", "EInt");
assertFeatureConfiguration(ruleA, 2, "featureC", "EString");
assertAttributeConfiguration(ruleA, 0, "featureA", "EString");
assertAttributeConfiguration(ruleA, 1, "featureB", "EInt");
assertAttributeConfiguration(ruleA, 2, "featureC", "EString");
}
public void testCardinalityOfFeatures() throws Exception {
@ -136,9 +147,9 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
assertNotNull(ruleA);
assertEquals(3, ruleA.getEAttributes().size());
assertFeatureConfiguration(ruleA, 0, "featureA", "EBoolean", 0, 1);
assertFeatureConfiguration(ruleA, 1, "featureB", "EInt", 0, 1);
assertFeatureConfiguration(ruleA, 2, "featureC", "EString", 0, -1);
assertAttributeConfiguration(ruleA, 0, "featureA", "EBoolean", 0, 1);
assertAttributeConfiguration(ruleA, 1, "featureB", "EInt", 0, 1);
assertAttributeConfiguration(ruleA, 2, "featureC", "EString", 0, -1);
}
public void testOptionalAssignmentsInGroup() throws Exception {
@ -148,8 +159,8 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
assertNotNull(ruleA);
assertEquals(2, ruleA.getEAttributes().size());
assertFeatureConfiguration(ruleA, 0, "featureA", "EBoolean", 0, 1);
assertFeatureConfiguration(ruleA, 1, "featureB", "EInt", 0, -1);
assertAttributeConfiguration(ruleA, 0, "featureA", "EBoolean", 0, 1);
assertAttributeConfiguration(ruleA, 1, "featureB", "EInt", 0, -1);
}
public void testFeaturesAndInheritanceOptionalRuleCall() throws Exception {
@ -162,10 +173,10 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
assertNotNull(ruleB);
assertEquals(1, ruleA.getEAttributes().size());
assertFeatureConfiguration(ruleA, 0, "featureA", "EInt");
assertAttributeConfiguration(ruleA, 0, "featureA", "EInt");
assertEquals(1, ruleB.getEAttributes().size());
assertFeatureConfiguration(ruleB, 0, "featureB", "EString");
assertAttributeConfiguration(ruleB, 0, "featureB", "EString");
}
public void testFeaturesAndInheritanceMandatoryRuleCall() throws Exception {
@ -180,8 +191,8 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
assertEquals(0, ruleA.getEAttributes().size());
assertEquals(2, ruleB.getEAttributes().size());
assertFeatureConfiguration(ruleB, 0, "featureA", "EInt");
assertFeatureConfiguration(ruleB, 1, "featureB", "EString");
assertAttributeConfiguration(ruleB, 0, "featureA", "EInt");
assertAttributeConfiguration(ruleB, 1, "featureB", "EString");
}
public void testFeaturesAndInheritanceOfMandatoryAlternativeRuleCalls() throws Exception {
@ -205,12 +216,12 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
// test all features are separated
assertEquals(0, ruleA.getEAttributes().size());
assertEquals(2, ruleB.getEAttributes().size());
assertFeatureConfiguration(ruleB, 0, "featureA", "EString");
assertFeatureConfiguration(ruleB, 1, "featureB", "EString");
assertAttributeConfiguration(ruleB, 0, "featureA", "EString");
assertAttributeConfiguration(ruleB, 1, "featureB", "EString");
assertEquals(3, ruleC.getEAttributes().size());
assertFeatureConfiguration(ruleC, 0, "featureC1", "EString");
assertFeatureConfiguration(ruleC, 1, "featureA", "EString");
assertFeatureConfiguration(ruleC, 2, "featureC2", "EString");
assertAttributeConfiguration(ruleC, 0, "featureC1", "EString");
assertAttributeConfiguration(ruleC, 1, "featureA", "EString");
assertAttributeConfiguration(ruleC, 2, "featureC2", "EString");
}
public void testFeaturesAndInheritanceOfOptionalOptionalRuleCalls() throws Exception {
@ -233,12 +244,12 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
// test all features are separated
assertEquals(1, ruleA.getEAttributes().size());
assertFeatureConfiguration(ruleA, 0, "featureA", "EString");
assertAttributeConfiguration(ruleA, 0, "featureA", "EString");
assertEquals(1, ruleB.getEAttributes().size());
assertFeatureConfiguration(ruleB, 0, "featureB", "EString");
assertAttributeConfiguration(ruleB, 0, "featureB", "EString");
assertEquals(2, ruleC.getEAttributes().size());
assertFeatureConfiguration(ruleC, 0, "featureC1", "EString");
assertFeatureConfiguration(ruleC, 1, "featureC2", "EString");
assertAttributeConfiguration(ruleC, 0, "featureC1", "EString");
assertAttributeConfiguration(ruleC, 1, "featureC2", "EString");
}
public void testFeaturesAndInheritanceOfNestedRuleCalls() throws Exception {
@ -265,20 +276,83 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
// test all features are separated
assertEquals(2, ruleA.getEAttributes().size());
assertFeatureConfiguration(ruleA, 0, "featureBC", "EString");
assertFeatureConfiguration(ruleA, 1, "featureA", "EString");
assertAttributeConfiguration(ruleA, 0, "featureBC", "EString");
assertAttributeConfiguration(ruleA, 1, "featureA", "EString");
assertEquals(1, ruleB.getEAttributes().size());
assertFeatureConfiguration(ruleB, 0, "featureB2", "EString");
assertAttributeConfiguration(ruleB, 0, "featureB2", "EString");
assertEquals(4, ruleC.getEAttributes().size());
assertFeatureConfiguration(ruleC, 0, "featureC1", "EString");
assertFeatureConfiguration(ruleC, 1, "featureCD", "EString");
assertFeatureConfiguration(ruleC, 2, "featureA", "EString");
assertFeatureConfiguration(ruleC, 3, "featureC2", "EString");
assertAttributeConfiguration(ruleC, 0, "featureC1", "EString");
assertAttributeConfiguration(ruleC, 1, "featureCD", "EString");
assertAttributeConfiguration(ruleC, 2, "featureA", "EString");
assertAttributeConfiguration(ruleC, 3, "featureC2", "EString");
assertEquals(4, ruleD.getEAttributes().size());
assertFeatureConfiguration(ruleD, 0, "featureD1", "EString");
assertFeatureConfiguration(ruleD, 1, "featureCD", "EString");
assertFeatureConfiguration(ruleD, 2, "featureA", "EString");
assertFeatureConfiguration(ruleD, 3, "featureD2", "EString");
assertAttributeConfiguration(ruleD, 0, "featureD1", "EString");
assertAttributeConfiguration(ruleD, 1, "featureCD", "EString");
assertAttributeConfiguration(ruleD, 2, "featureA", "EString");
assertAttributeConfiguration(ruleD, 3, "featureD2", "EString");
}
public void testAssignedRuleCall() throws Exception {
final String grammar = "language test generate test 'http://test' RuleA: callA1=RuleB callA2+=RuleB simpleFeature=ID; RuleB: featureB=ID;";
EPackage ePackage = getEPackageFromGrammar(grammar);
assertEquals(2, ePackage.getEClassifiers().size());
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
assertNotNull(ruleA);
EClass ruleB = (EClass) ePackage.getEClassifier("RuleB");
assertNotNull(ruleB);
assertEquals(1, ruleA.getEAttributes().size());
assertAttributeConfiguration(ruleA, 0, "simpleFeature", "EString");
assertEquals(2, ruleA.getEReferences().size());
assertReferenceConfiguration(ruleA, 0, "callA1", "RuleB", 0, 1);
assertReferenceConfiguration(ruleA, 1, "callA2", "RuleB", 0, -1);
assertEquals(1, ruleB.getEAttributes().size());
assertAttributeConfiguration(ruleB, 0, "featureB", "EString");
}
public void testAssignedCrossReference() throws Exception {
final String grammar = "language test generate test 'http://test' RuleA: refA1=[TypeB] refA2+=[TypeB|RuleB] simpleFeature=ID; RuleB returns TypeB: featureB=ID;";
EPackage ePackage = getEPackageFromGrammar(grammar);
assertEquals(2, ePackage.getEClassifiers().size());
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
assertNotNull(ruleA);
EClass typeB = (EClass) ePackage.getEClassifier("TypeB");
assertNotNull(typeB);
assertEquals(1, ruleA.getEAttributes().size());
assertAttributeConfiguration(ruleA, 0, "simpleFeature", "EString");
assertEquals(2, ruleA.getEReferences().size());
assertReferenceConfiguration(ruleA, 0, "refA1", "TypeB", 0, 1);
assertReferenceConfiguration(ruleA, 1, "refA2", "TypeB", 0, -1);
assertEquals(1, typeB.getEAttributes().size());
assertAttributeConfiguration(typeB, 0, "featureB", "EString");
}
public void testAssignedParenthesizedElement() throws Exception {
final String grammar = "language test generate test 'http://test' RuleA: featureA1?=(RuleB) refA1=(RuleB) refA2=(RuleB|RuleC) refA3+=(RuleB|RuleC|RuleD) refA4=(RuleB|RuleD) featureA2+=('a'|'b'); RuleB returns TypeB: RuleC? featureB=ID; RuleC: featureC=ID; RuleD returns TypeB: featureD=ID;";
EPackage ePackage = getEPackageFromGrammar(grammar);
assertEquals(3, ePackage.getEClassifiers().size());
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
assertNotNull(ruleA);
assertEquals(0, ruleA.getESuperTypes().size());
EClass typeB = (EClass) ePackage.getEClassifier("TypeB");
assertNotNull(typeB);
assertEquals(0, typeB.getESuperTypes().size());
EClass ruleC = (EClass) ePackage.getEClassifier("RuleC");
assertNotNull(ruleC);
assertEquals(1, ruleC.getESuperTypes().size());
assertEquals(typeB, ruleC.getESuperTypes().get(0));
assertEquals(2, ruleA.getEAttributes().size());
assertAttributeConfiguration(ruleA, 0, "featureA1", "EBoolean");
assertAttributeConfiguration(ruleA, 1, "featureA2", "EString", 0, -1);
assertEquals(4, ruleA.getEReferences().size());
assertReferenceConfiguration(ruleA, 0, "refA1", "TypeB", 0, 1);
// TODO should be common compatible type according to #248430
assertReferenceConfiguration(ruleA, 1, "refA2", "EObject", 0, 1);
assertReferenceConfiguration(ruleA, 2, "refA3", "EObject", 0, -1);
assertReferenceConfiguration(ruleA, 3, "refA4", "TypeB", 0, 1);
}
}