From 17a48a5cc5d057015ee7bc11d56c7485e7efeb78 Mon Sep 17 00:00:00 2001 From: jkohnlein Date: Mon, 22 Sep 2008 09:21:51 +0000 Subject: [PATCH] Applied patch from heiko --- .../src/org/eclipse/xtext/GrammarUtil.java | 23 +- .../parser/GenericEcoreElementFactory.java | 15 +- .../xtext/resource/metamodel/EClassInfo.java | 43 ---- .../resource/metamodel/EClassifierInfo.java | 108 ++++++++ ...EClassInfos.java => EClassifierInfos.java} | 33 ++- .../metamodel/Xtext2EcoreTransformer.java | 241 +++++++++++++----- .../xtext/generator/AutoTestSuite.java | 1 + .../Xtext2EcoreTransformerTests.java | 125 ++++++++- 8 files changed, 442 insertions(+), 147 deletions(-) delete mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassInfo.java create mode 100644 plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassifierInfo.java rename plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/{EClassInfos.java => EClassifierInfos.java} (52%) diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java index 79896cd22..43e082139 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/GrammarUtil.java @@ -27,6 +27,7 @@ import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xtext.builtin.IXtextBuiltin; +import org.eclipse.xtext.impl.TypeRefImpl; import org.eclipse.xtext.util.Strings; /** @@ -256,10 +257,26 @@ public class GrammarUtil { return getQualifiedName(rule.getType()); } - public static String getQualifiedName(TypeRef type) { - return (type.getAlias() != null ? type.getAlias() + "::" : "") + type.getName(); + public static String getQualifiedName(String alias, String name) { + return (alias != null ? alias + "::" : "") + name; } - + + public static String getQualifiedName(TypeRef type) { + return getQualifiedName(type.getAlias(), type.getName()); + } + + public static TypeRef getTypeRef(String qualifiedName) { + TypeRef result = XtextFactory.eINSTANCE.createTypeRef(); + String[] split = qualifiedName.split("::"); + if (split.length > 1) { + result.setAlias(split[0]); + result.setName(split[1]); + } else { + result.setName(qualifiedName); + } + return result; + } + public static boolean isAssigned(EObject e) { return containingAssignment(e) != null; } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java index e0f8f8c9c..b85072200 100755 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/parser/GenericEcoreElementFactory.java @@ -26,6 +26,7 @@ import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.IGrammarAccess; import org.eclipse.xtext.IMetamodelAccess; import org.eclipse.xtext.ReferencedMetamodel; +import org.eclipse.xtext.TypeRef; import org.eclipse.xtext.conversion.IValueConverterService; import org.eclipse.xtext.service.Inject; @@ -119,18 +120,12 @@ public class GenericEcoreElementFactory implements IAstFactory { } public EClass getEClass(String fullTypeName) { - String[] split = fullTypeName.split("::"); - String typeName = fullTypeName; - String alias = null; - if (split.length > 1) { - alias = split[0]; - typeName = split[1]; - } - EPackage pack = getEPackage(alias); + TypeRef typeRef = GrammarUtil.getTypeRef(fullTypeName); + EPackage pack = getEPackage(typeRef.getAlias()); if (pack == null) { - throw new IllegalStateException("Couldn't find any epackages for alias '" + alias + "'"); + throw new IllegalStateException("Couldn't find any epackages for alias '" + typeRef.getAlias() + "'"); } - EClassifier classifier = pack.getEClassifier(typeName); + EClassifier classifier = pack.getEClassifier(typeRef.getName()); if (classifier instanceof EClass) { return (EClass) classifier; } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassInfo.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassInfo.java deleted file mode 100644 index c9de47c94..000000000 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassInfo.java +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************************* - * 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.resource.metamodel; - -import java.util.HashSet; -import java.util.Set; - -import org.eclipse.emf.ecore.EClass; - -/** - * @author Jan Köhnlein - Initial contribution and API - * - */ -public class EClassInfo { - - private EClass eClass; - private boolean isGenerated; - private Set superTypes = new HashSet(); - - public EClassInfo(EClass metaType, boolean isGenerated) { - super(); - this.isGenerated = isGenerated; - this.eClass = metaType; - } - - public EClass getEClass() { - return eClass; - } - - public boolean isGenerated() { - return isGenerated; - } - - public boolean addSupertype(EClassInfo superTypeInfo) { - return superTypes.add(superTypeInfo); - } -} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassifierInfo.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassifierInfo.java new file mode 100644 index 000000000..52d964641 --- /dev/null +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassifierInfo.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * 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.resource.metamodel; + +import java.util.HashSet; +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.EDataType; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EcoreFactory; + +/** + * @author Jan K?hnlein - Initial contribution and API + * + */ +public abstract class EClassifierInfo { + + private EClassifier eClassifier; + private boolean isGenerated; + + private EClassifierInfo(EClassifier metaType, boolean isGenerated) { + super(); + this.isGenerated = isGenerated; + this.eClassifier = metaType; + } + + public static EClassifierInfo createEClassInfo(EClass eClass, boolean isGenerated) { + return new EClassInfo(eClass, isGenerated); + } + + public static EClassifierInfo createEDataTypeInfo(EDataType eDataType, boolean isGenerated) { + return new EDataTypeInfo(eDataType, isGenerated); + } + + public EClassifier getEClass() { + return eClassifier; + } + + public boolean isGenerated() { + return isGenerated; + } + + public abstract boolean addSupertype(EClassifierInfo superTypeInfo); + public abstract boolean addFeature(String featureName, EClassifierInfo featureType); + + static class EClassInfo extends EClassifierInfo { + + public EClassInfo(EClassifier metaType, boolean isGenerated) { + super(metaType, isGenerated); + } + + @Override + public boolean addSupertype(EClassifierInfo superTypeInfo) { + if(!isGenerated()){ + throw new IllegalStateException("Generated Type cannot be modified."); + } + if(!(superTypeInfo instanceof EClassInfo)) { + throw new IllegalArgumentException("superTypeInfo must represent EClass"); + } + EClass eClass = (EClass)getEClass(); + EClass superEClass = (EClass)superTypeInfo.getEClass(); + return eClass.getESuperTypes().add(superEClass); + } + + @Override + public boolean addFeature(String featureName, + EClassifierInfo featureType) { + + EClassifier featureClassifier = featureType.getEClass(); + + EAttribute attribute = EcoreFactory.eINSTANCE.createEAttribute(); + attribute.setName(featureName); + attribute.setEType(featureClassifier); + + EList features = ((EClass)getEClass()).getEStructuralFeatures(); + return features.add(attribute); + } + } + + static class EDataTypeInfo extends EClassifierInfo { + + public EDataTypeInfo(EClassifier metaType, boolean isGenerated) { + super(metaType, isGenerated); + } + + @Override + public boolean addSupertype(EClassifierInfo superTypeInfo) { + throw new UnsupportedOperationException("Cannot add supertype to simple datatype"); + } + + @Override + public boolean addFeature(String featureName, + EClassifierInfo featureType) { + throw new UnsupportedOperationException("Cannot add feature to simple datatype"); + } + + } +} diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassInfos.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassifierInfos.java similarity index 52% rename from plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassInfos.java rename to plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassifierInfos.java index ce2380f95..5f6311cae 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassInfos.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/EClassifierInfos.java @@ -11,6 +11,7 @@ package org.eclipse.xtext.resource.metamodel; import java.util.HashMap; import java.util.Map; +import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.TypeRef; import org.eclipse.xtext.util.Pair; import org.eclipse.xtext.util.Strings; @@ -20,33 +21,31 @@ import org.eclipse.xtext.util.Strings; * 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 EClassInfos { +public class EClassifierInfos { - class Key extends Pair { - public Key(String firstElement, String secondElement) { - super(firstElement, secondElement); - } + private Map infoMap = new HashMap(); + + public boolean addInfo(TypeRef typeRef, EClassifierInfo metatypeInfo) { + return infoMap.put(GrammarUtil.getQualifiedName(typeRef), metatypeInfo) != metatypeInfo; } - private Map infoMap = new HashMap(); - - public boolean addInfo(TypeRef typeRef, EClassInfo metatypeInfo) { - return infoMap.put(key(typeRef), metatypeInfo) != metatypeInfo; + public boolean addInfo(String alias, String name, EClassifierInfo metatypeInfo) { + return infoMap.put(GrammarUtil.getQualifiedName(alias, name), metatypeInfo) != metatypeInfo; } - public boolean addInfo(String alias, String name, EClassInfo metatypeInfo) { - return infoMap.put(new Key(alias, name), metatypeInfo) != metatypeInfo; + public EClassifierInfo getInfo(TypeRef typeRef) { + return getInfo(GrammarUtil.getQualifiedName(typeRef)); } - public EClassInfo getInfo(TypeRef typeRef) { - return infoMap.get(key(typeRef)); + public EClassifierInfo getInfo(String qualifiedName) { + return infoMap.get(qualifiedName); } - private Key key(TypeRef typeRef) { - return new Key(Strings.emptyIfNull(typeRef.getAlias()), typeRef.getName()); + public void addAll(EClassifierInfos classInfos) { + infoMap.putAll(classInfos.infoMap); } - + } diff --git a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformer.java b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformer.java index 246940d05..bd816a980 100644 --- a/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformer.java +++ b/plugins/org.eclipse.xtext/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformer.java @@ -14,8 +14,10 @@ import java.util.List; import java.util.Map; 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; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EcoreFactory; @@ -24,15 +26,16 @@ import org.eclipse.xtext.AbstractMetamodelDeclaration; import org.eclipse.xtext.AbstractRule; import org.eclipse.xtext.Action; import org.eclipse.xtext.Alternatives; +import org.eclipse.xtext.Assignment; import org.eclipse.xtext.GeneratedMetamodel; import org.eclipse.xtext.Grammar; import org.eclipse.xtext.GrammarUtil; import org.eclipse.xtext.Group; +import org.eclipse.xtext.LexerRule; import org.eclipse.xtext.ParserRule; import org.eclipse.xtext.ReferencedMetamodel; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.TypeRef; -import org.eclipse.xtext.XtextFactory; import org.eclipse.xtext.util.Strings; /** @@ -44,7 +47,7 @@ public class Xtext2EcoreTransformer { private Grammar grammar; private Map generatedEPackages; private Grammar superGrammar; - private EClassInfos eClassInfos; + private EClassifierInfos eClassifierInfos; public Xtext2EcoreTransformer() { } @@ -58,7 +61,9 @@ public class Xtext2EcoreTransformer { this.grammar = grammar; generatedEPackages = new HashMap(); superGrammar = GrammarUtil.getSuperGrammar(grammar); - eClassInfos = new EClassInfos(); + eClassifierInfos = new EClassifierInfos(); + if (superGrammar != null) + collectEClassInfosOfSuperGrammar(); collectEPackages(); // create types: @@ -66,19 +71,14 @@ public class Xtext2EcoreTransformer { // - typeref in actions for (AbstractRule rule : grammar.getRules()) { // - return types (lexer and parser rules) - TypeRef ruleReturnTypeRef = rule.getType(); try { - if (ruleReturnTypeRef == null) { - ruleReturnTypeRef = XtextFactory.eINSTANCE.createTypeRef(); - ruleReturnTypeRef.setName(rule.getName()); - } - EClassInfo generatedEClass = findOrCreateEClass(ruleReturnTypeRef); + EClassifierInfo generatedEClass = findOrCreateEClass(rule); if (rule instanceof ParserRule) { ParserRule parserRule = (ParserRule) rule; - deriveTypesAndHierarchy(generatedEClass, parserRule.getAlternatives()); + deriveTypesAndHierarchy(generatedEClass, parserRule + .getAlternatives()); } - } - catch (TransformationException e) { + } catch (TransformationException e) { reportError(e.getMessage(), e.getErroneousElement()); } } @@ -88,6 +88,15 @@ public class Xtext2EcoreTransformer { // - assignments // - feature in actions // multiplicity! + for (AbstractRule rule : grammar.getRules()) { + try { + if (rule instanceof ParserRule) { + this.deriveFeatures((ParserRule) rule); + } + } catch (TransformationException e) { + reportError(e.getMessage(), e.getErroneousElement()); + } + } // type hierarchy // - rule calls (optionality) @@ -102,6 +111,51 @@ public class Xtext2EcoreTransformer { return new ArrayList(generatedEPackages.values()); } + private void collectEClassInfosOfSuperGrammar() { + Xtext2EcoreTransformer transformer = new Xtext2EcoreTransformer(); + transformer.transform(superGrammar); + this.getEClassifierInfos().addAll(transformer.getEClassifierInfos()); + } + + private InterpretationContext deriveFeatures(InterpretationContext context, + AbstractElement element) throws TransformationException { + if (element instanceof Assignment) { + Assignment assignment = (Assignment) element; + context.addFeature(assignment); + } else if (element instanceof Group) { + Group group = (Group) element; + return deriveFeatures(context, group.getAbstractTokens()); + } + + return context; + } + + private InterpretationContext deriveFeatures(InterpretationContext context, + EList elements) throws TransformationException { + for(AbstractElement element : elements) { + context = deriveFeatures(context, element); + } + return context; + } + + private void deriveFeatures(ParserRule rule) throws TransformationException { + EClassifierInfo classInfo = findOrCreateEClass(rule); + boolean isGenerated = classInfo.isGenerated(); + InterpretationContext context = new InterpretationContext(classInfo, + isGenerated, true); + deriveFeatures(context, rule.getAlternatives()); + } + + // TODO : Try to get rid of + private TypeRef getOrFakeReturnType(AbstractRule rule) { + TypeRef result = rule.getType(); + if (result == null) { + String returnTypeName = GrammarUtil.getReturnTypeName(rule); + result = GrammarUtil.getTypeRef(returnTypeName); + } + return result; + } + private void fillGeneratedPackages() { } @@ -110,101 +164,132 @@ public class Xtext2EcoreTransformer { * @param alternatives * @throws TransformationException */ - private void deriveTypesAndHierarchy(EClassInfo ruleReturnType, AbstractElement element) - throws TransformationException { + private void deriveTypesAndHierarchy(EClassifierInfo ruleReturnType, + AbstractElement element) throws TransformationException { if (element instanceof RuleCall) { RuleCall ruleCall = (RuleCall) element; AbstractRule calledRule = GrammarUtil.calledRule(ruleCall); - TypeRef calledRuleReturnTypeRef = calledRule.getType(); + TypeRef calledRuleReturnTypeRef = getOrFakeReturnType(calledRule); addSuperType(calledRuleReturnTypeRef, ruleReturnType); - } - else if (element instanceof Action) { + } else if (element instanceof Action) { Action action = (Action) element; TypeRef actionTypeRef = action.getTypeName(); addSuperType(actionTypeRef, ruleReturnType); - } - else if (element instanceof Group) { + } else if (element instanceof Group) { Group group = (Group) element; deriveTypesAndHierarchy(ruleReturnType, group.getAbstractTokens()); - } - else if (element instanceof Alternatives) { + } else if (element instanceof Alternatives) { Alternatives alternatives = (Alternatives) element; deriveTypesAndHierarchy(ruleReturnType, alternatives.getGroups()); } } - private void deriveTypesAndHierarchy(EClassInfo ruleReturnType, List elements) - throws TransformationException { + private void deriveTypesAndHierarchy(EClassifierInfo ruleReturnType, + List elements) throws TransformationException { for (AbstractElement element : elements) { deriveTypesAndHierarchy(ruleReturnType, element); } } - private void addSuperType(TypeRef subTypeRef, EClassInfo superType) throws TransformationException { - EClassInfo calledRuleReturnType = findOrCreateEClass(subTypeRef); + private void addSuperType(TypeRef subTypeRef, EClassifierInfo superType) + throws TransformationException { + EClassifierInfo calledRuleReturnType = findOrCreateEClass(subTypeRef); calledRuleReturnType.addSupertype(superType); } class InterpretationContext { - public InterpretationContext(EClass currentType, boolean isGeneratedType, boolean isRuleCallAllowed) { + public InterpretationContext(EClassifierInfo currentType, + boolean isGeneratedType, boolean isRuleCallAllowed) { super(); this.currentType = currentType; this.isGeneratedType = isGeneratedType; this.isRuleCallAllowed = isRuleCallAllowed; } - public InterpretationContext clone() { - return new InterpretationContext(currentType, isGeneratedType, isRuleCallAllowed); + public void addFeature(Assignment assignment) + throws TransformationException { + String featureName = assignment.getFeature(); + RuleCall featureRuleCall = (RuleCall) assignment.getTerminal(); + AbstractRule featureTypeRule = GrammarUtil + .calledRule(featureRuleCall); + String featureTypeName = GrammarUtil + .getReturnTypeName(featureTypeRule); + EClassifierInfo featureTypeInfo = eClassifierInfos + .getInfo(featureTypeName); + if (featureTypeInfo == null) { + throw new TransformationException( + "Cannot resolve type of feature", assignment); + } + + currentType.addFeature(featureName, featureTypeInfo); + + // TODO : fill Cardinality of attribute + // this.currentType.getEStructuralFeatures().add(attribute); } - EClass currentType; + public InterpretationContext clone() { + return new InterpretationContext(currentType, isGeneratedType, + isRuleCallAllowed); + } + + // TODO : Use set of types to reflect mandatory actions + EClassifierInfo currentType; boolean isGeneratedType; boolean isRuleCallAllowed = true; } - private InterpretationContext interpretElement(AbstractElement element, EClass returnType) { - // TODO: implement - return null; - } - private void collectEPackages() { - EList metamodelDeclarations = grammar.getMetamodelDeclarations(); + EList metamodelDeclarations = grammar + .getMetamodelDeclarations(); for (AbstractMetamodelDeclaration metamodelDeclaration : metamodelDeclarations) { if (metamodelDeclaration instanceof ReferencedMetamodel) { // load imported metamodel ReferencedMetamodel referencedMetamodel = (ReferencedMetamodel) metamodelDeclaration; - EPackage referencedEPackage = GrammarUtil.loadEPackage(referencedMetamodel); + EPackage referencedEPackage = GrammarUtil + .loadEPackage(referencedMetamodel); if (referencedEPackage == null) { - reportError("Cannot not load metamodel " + referencedMetamodel.getUri(), referencedMetamodel); - } - else { + reportError("Cannot not load metamodel " + + referencedMetamodel.getUri(), referencedMetamodel); + } else { String alias = referencedMetamodel.getAlias(); if (Strings.isEmpty(alias)) { - reportError("Referenced metamodels must have an alias", referencedMetamodel); - } - else { - for (EClassifier eClassifier : referencedEPackage.getEClassifiers()) { - if (eClassifier instanceof EClass) { - EClassInfo info = new EClassInfo((EClass) eClassifier, false); - eClassInfos.addInfo(alias, eClassifier.getName(), info); - } - } + reportError("Referenced metamodels must have an alias", + referencedMetamodel); + } else { + collectClassInfosOf(referencedEPackage, alias); } } - } - else if (metamodelDeclaration instanceof GeneratedMetamodel) { + } else if (metamodelDeclaration instanceof GeneratedMetamodel) { // instantiate EPackages for generated metamodel GeneratedMetamodel generatedMetamodel = (GeneratedMetamodel) metamodelDeclaration; - EPackage generatedEPackage = EcoreFactory.eINSTANCE.createEPackage(); + EPackage generatedEPackage = EcoreFactory.eINSTANCE + .createEPackage(); generatedEPackage.setName(generatedMetamodel.getName()); generatedEPackage.setNsPrefix(generatedMetamodel.getName()); generatedEPackage.setNsURI(generatedMetamodel.getNsURI()); - String alias = Strings.emptyIfNull(generatedMetamodel.getAlias()); + String alias = Strings.emptyIfNull(generatedMetamodel + .getAlias()); generatedEPackages.put(alias, generatedEPackage); } } } + private void collectClassInfosOf(EPackage referencedEPackage, String alias) { + for (EClassifier eClassifier : referencedEPackage.getEClassifiers()) { + if (eClassifier instanceof EClass) { + EClass eClass = (EClass) eClassifier; + EClassifierInfo info = EClassifierInfo.createEClassInfo(eClass, + false); + eClassifierInfos.addInfo(alias, eClassifier.getName(), info); + } else if (eClassifier instanceof EDataType) { + EDataType eDataType = (EDataType) eClassifier; + EClassifierInfo info = EClassifierInfo.createEDataTypeInfo( + eDataType, false); + eClassifierInfos.addInfo(alias, eClassifier.getName(), info); + } + } + } + /** * @param string * @param generatedMetamodel @@ -214,26 +299,50 @@ public class Xtext2EcoreTransformer { } - private void raiseError(String message, EObject erroneousElement) throws TransformationException { + private void raiseError(String message, EObject erroneousElement) + throws TransformationException { throw new TransformationException(message, erroneousElement); } - private EClassInfo findOrCreateEClass(TypeRef typeRef) throws TransformationException { - EClassInfo info = eClassInfos.getInfo(typeRef); + private EClassifierInfo findOrCreateEClass(AbstractRule rule) + throws TransformationException { + TypeRef typeRef = getOrFakeReturnType(rule); + return findOrCreateEClass(typeRef); + } + + private EClassifierInfo findOrCreateEClass(TypeRef typeRef) + throws TransformationException { + EClassifierInfo info = eClassifierInfos.getInfo(typeRef); if (info == null) { - String typeRefAlias = Strings.emptyIfNull(typeRef.getAlias()); - String typeRefName = typeRef.getName(); - EPackage generatedEPackage = generatedEPackages.get(typeRefAlias); - if (generatedEPackage == null) { - raiseError("Cannot create type in alias " + typeRefAlias, typeRef); - } - EClass generatedEClass = EcoreFactory.eINSTANCE.createEClass(); - generatedEClass.setName(typeRefName); - generatedEPackage.getEClassifiers().add(generatedEClass); - info = new EClassInfo(generatedEClass, true); - eClassInfos.addInfo(typeRef, info); + info = createEClass(typeRef); } return info; } + private EClassifierInfo createEClass(TypeRef typeRef) + throws TransformationException { + if (eClassifierInfos.getInfo(typeRef) != null) + throw new IllegalArgumentException( + "Cannot create EClass for same type twice" + + GrammarUtil.getQualifiedName(typeRef)); + + EClassifierInfo info; + String typeRefAlias = Strings.emptyIfNull(typeRef.getAlias()); + String typeRefName = typeRef.getName(); + EPackage generatedEPackage = generatedEPackages.get(typeRefAlias); + if (generatedEPackage == null) { + raiseError("Cannot create type in alias " + typeRefAlias, typeRef); + } + EClass generatedEClass = EcoreFactory.eINSTANCE.createEClass(); + generatedEClass.setName(typeRefName); + generatedEPackage.getEClassifiers().add(generatedEClass); + info = EClassifierInfo.createEClassInfo(generatedEClass, true); + eClassifierInfos.addInfo(typeRef, info); + return info; + } + + public EClassifierInfos getEClassifierInfos() { + return eClassifierInfos; + } + } diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/generator/AutoTestSuite.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/generator/AutoTestSuite.java index eb3c56255..69a36e8fe 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/generator/AutoTestSuite.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/generator/AutoTestSuite.java @@ -40,6 +40,7 @@ public class AutoTestSuite { suite.addTestSuite(org.eclipse.xtext.parsetree.reconstr.WhitespacePreservingCallbackTest.class); suite.addTestSuite(org.eclipse.xtext.reference.CommentOnEofBug_234135_Test.class); suite.addTestSuite(org.eclipse.xtext.reference.LeafNodeBug_234132_Test.class); + suite.addTestSuite(org.eclipse.xtext.resource.metamodel.Xtext2EcoreTransformerTests.class); suite.addTestSuite(org.eclipse.xtext.typeresolution.XtextUtilConstructionTest.class); suite.addTestSuite(org.eclipse.xtext.xtext2ecore.ActionTest.class); suite.addTestSuite(org.eclipse.xtext.xtext2ecore.BootstrapModelTest.class); diff --git a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformerTests.java b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformerTests.java index c42b1f9cc..712f5521e 100644 --- a/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformerTests.java +++ b/tests/org.eclipse.xtext.generator.tests/src/org/eclipse/xtext/resource/metamodel/Xtext2EcoreTransformerTests.java @@ -7,11 +7,16 @@ *******************************************************************************/ 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.xtext.Grammar; import org.eclipse.xtext.XtextStandaloneSetup; @@ -28,18 +33,122 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest { with(XtextStandaloneSetup.class); } - public void testRuleWithoutExplicitReturnType() throws Exception { - Grammar grammar = (Grammar) getModel("language test generate test 'http://test' MyRule: myFeature=INT;"); - EList rules = grammar.getRules(); - AbstractRule firstRule = rules.get(0); - assertNull(firstRule.getType()); + private EPackage getEPackageFromGrammar(String xtextGrammar) + throws Exception { + Grammar grammar = (Grammar) getModel(xtextGrammar); Xtext2EcoreTransformer xtext2EcoreTransformer = new Xtext2EcoreTransformer(); List metamodels = xtext2EcoreTransformer.transform(grammar); - assertTrue(metamodels != null && !metamodels.isEmpty()); - EPackage firstEPackage = metamodels.get(0); - EList classifiers = firstEPackage.getEClassifiers(); + assertNotNull(metamodels); + assertEquals(1, metamodels.size()); + + EPackage result = metamodels.get(0); + assertNotNull(result); + return result; + } + + private void assertFeatureConfiguration(EClass eClass, int attributeIndex, + String featureName, String featureTypeName) { + EAttribute feature = eClass.getEAttributes().get(attributeIndex); + assertEquals(featureName, feature.getName()); + assertNotNull(feature.getEType()); + assertEquals(featureTypeName, feature.getEType().getName()); + } + + public void testTypesOfImplicitSuperGrammar() throws Exception { + final String xtextGrammar = "language test generate test 'http://test' MyRule: myFeature=INT;"; + Grammar grammar = (Grammar) getModel(xtextGrammar); + Xtext2EcoreTransformer transformer = new Xtext2EcoreTransformer(); + transformer.transform(grammar); + // directly from grammar + assertNotNull(transformer.getEClassifierInfos().getInfo("MyRule")); + // ecore data types + assertNotNull(transformer.getEClassifierInfos().getInfo("ecore::EString")); + assertNotNull(transformer.getEClassifierInfos().getInfo("ecore::EInt")); + } + + public void testRuleWithoutExplicitReturnType() throws Exception { + final String grammar = "language test generate test 'http://test' MyRule: myFeature=INT;"; + EPackage ePackage = getEPackageFromGrammar(grammar); + EList classifiers = ePackage.getEClassifiers(); assertEquals(1, classifiers.size()); EClassifier implicitlyDefinedMetatype = classifiers.get(0); assertEquals("MyRule", implicitlyDefinedMetatype.getName()); } + + public void testRulesWithExplicitReturnType() throws Exception { + final String grammar = "language test generate test 'http://test' RuleA returns TypeA: featureA=INT; RuleB returns TypeB: featureB= INT;"; + EPackage ePackage = getEPackageFromGrammar(grammar); + assertEquals(2, ePackage.getEClassifiers().size()); + assertNotNull(ePackage.getEClassifier("TypeA")); + assertNotNull(ePackage.getEClassifier("TypeB")); + } + + public void testSimpleHierarchy() throws Exception { + final String grammar = "language test generate test 'http://test' RuleA: RuleB; RuleB: featureB= INT;"; + 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); + assertTrue(ruleA.getESuperTypes().isEmpty()); + assertEquals(1, ruleB.getESuperTypes().size()); + EClass superClass = ruleB.getESuperTypes().get(0); + assertEquals(ruleA, superClass); + } + + public void testSingleFeatures() throws Exception { + final String grammar = "language test generate test 'http://test' RuleA: featureA=INT;"; + EPackage ePackage = getEPackageFromGrammar(grammar); + EClass ruleA = (EClass) ePackage.getEClassifier("RuleA"); + assertNotNull(ruleA); + + assertEquals(1, ruleA.getEAttributes().size()); + assertFeatureConfiguration(ruleA, 0, "featureA", "EInt"); + } + + public void testBuiltInFeatureTypes() throws Exception { + final String grammar = "language test generate test 'http://test' RuleA: featureA=ID featureB=INT featureC=STRING;"; + EPackage ePackage = getEPackageFromGrammar(grammar); + EClass ruleA = (EClass) ePackage.getEClassifier("RuleA"); + assertNotNull(ruleA); + + assertEquals(3, ruleA.getEAttributes().size()); + assertFeatureConfiguration(ruleA, 0, "featureA", "EString"); + assertFeatureConfiguration(ruleA, 1, "featureB", "EInt"); + assertFeatureConfiguration(ruleA, 2, "featureC", "EString"); + } + + public void testFeatureAndInheritanceOptionalRuleCall() throws Exception { + final String grammar = "language test generate test 'http://test' RuleA: RuleB? featureA=INT; RuleB: featureB=STRING;"; + 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()); + assertFeatureConfiguration(ruleA, 0, "featureA", "EInt"); + + assertEquals(1, ruleB.getEAttributes().size()); + assertFeatureConfiguration(ruleB, 0, "featureB", "EString"); + } + + public void _testFeatureAndInheritanceMandatoryRuleCall() throws Exception { + final String grammar = "language test generate test 'http://test' RuleA: RuleB featureA=INT; RuleB: featureB=STRING;"; + 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(0, ruleA.getEAttributes().size()); + + assertEquals(2, ruleB.getEAttributes().size()); + assertFeatureConfiguration(ruleB, 0, "featureA", "EInt"); + assertFeatureConfiguration(ruleB, 1, "featureB", "EString"); + } + }