mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 16:58:56 +00:00
Noch ein Patch from Heiko Behrens (Knut) BUG 247406 [Core] Reimplement derivation of metamodel from Xtext grammar in Java
This commit is contained in:
parent
54055641bc
commit
9a0da686a7
9 changed files with 146 additions and 46 deletions
|
@ -9,8 +9,11 @@
|
|||
|
||||
package org.eclipse.xtext;
|
||||
|
||||
import static org.eclipse.emf.ecore.util.EcoreUtil.*;
|
||||
import static org.eclipse.xtext.EcoreUtil2.*;
|
||||
import static org.eclipse.emf.ecore.util.EcoreUtil.getRootContainer;
|
||||
import static org.eclipse.xtext.EcoreUtil2.eAllContentsAsList;
|
||||
import static org.eclipse.xtext.EcoreUtil2.getAllContentsOfType;
|
||||
import static org.eclipse.xtext.EcoreUtil2.getContainerOfType;
|
||||
import static org.eclipse.xtext.EcoreUtil2.typeSelect;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
|
|
@ -11,8 +11,6 @@ package org.eclipse.xtext.parsetree;
|
|||
import org.eclipse.emf.common.notify.Adapter;
|
||||
import org.eclipse.emf.common.notify.Notification;
|
||||
import org.eclipse.emf.common.notify.Notifier;
|
||||
import org.eclipse.xtext.parsetree.AbstractNode;
|
||||
import org.eclipse.xtext.parsetree.CompositeNode;
|
||||
|
||||
public class NodeAdapter implements Adapter {
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ package org.eclipse.xtext.parsetree;
|
|||
import org.eclipse.emf.common.notify.Adapter;
|
||||
import org.eclipse.emf.common.notify.Notifier;
|
||||
import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl;
|
||||
import org.eclipse.xtext.parsetree.AbstractNode;
|
||||
|
||||
public class NodeAdapterFactory extends AdapterFactoryImpl {
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*******************************************************************************
|
||||
* 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 org.eclipse.emf.ecore.EObject;
|
||||
|
||||
/**
|
||||
* @author Heiko Behrens - Initial contribution and API
|
||||
*
|
||||
*/
|
||||
public interface ErrorAcceptor {
|
||||
public enum ErrorCode {
|
||||
NoSuchTypeAvailable,
|
||||
MoreThanOneTypeChangeInOneRule,
|
||||
CannotLoadMetamodel,
|
||||
MissingAliasForReferencedMetamodel,
|
||||
CannotCreateTypeInSealedMetamodel
|
||||
}
|
||||
|
||||
public void acceptError(ErrorCode errorCode, String message, EObject element);
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.xtext.resource.metamodel.ErrorAcceptor.ErrorCode;
|
||||
|
||||
/**
|
||||
* @author Jan Köhnlein - Initial contribution and API
|
||||
|
@ -20,10 +21,22 @@ public class TransformationException extends Exception {
|
|||
|
||||
private EObject erroneousElement;
|
||||
|
||||
public TransformationException(String message, EObject erroneousElement) {
|
||||
private ErrorCode errorCode;
|
||||
|
||||
public TransformationException(ErrorCode errorCode, String message, EObject erroneousElement) {
|
||||
super(message);
|
||||
this.errorCode = errorCode;
|
||||
this.erroneousElement = erroneousElement;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public TransformationException(ErrorCode errorCode, String message) {
|
||||
this(errorCode, message, null);
|
||||
}
|
||||
|
||||
public ErrorCode getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
public EObject getErroneousElement() {
|
||||
return erroneousElement;
|
||||
|
@ -32,5 +45,5 @@ public class TransformationException extends Exception {
|
|||
public void setErroneousElement(EObject erroneousElement) {
|
||||
this.erroneousElement = erroneousElement;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.AbstractRule;
|
||||
import org.eclipse.xtext.Alternatives;
|
||||
|
@ -22,6 +23,7 @@ import org.eclipse.xtext.GrammarUtil;
|
|||
import org.eclipse.xtext.Group;
|
||||
import org.eclipse.xtext.Keyword;
|
||||
import org.eclipse.xtext.RuleCall;
|
||||
import org.eclipse.xtext.resource.metamodel.ErrorAcceptor.ErrorCode;
|
||||
|
||||
/**
|
||||
* @author Heiko Behrens - Initial contribution and API
|
||||
|
@ -51,9 +53,10 @@ public class Xtext2ECoreInterpretationContext {
|
|||
}
|
||||
|
||||
public Xtext2ECoreInterpretationContext(EClassifierInfo newType, EClassifierInfos classifierInfos,
|
||||
boolean isRuleCallAllowed2) {
|
||||
boolean isRuleCallAllowed) {
|
||||
this(classifierInfos);
|
||||
this.currentTypes.add(newType);
|
||||
this.isRuleCallAllowed = isRuleCallAllowed;
|
||||
}
|
||||
|
||||
public void addFeature(Assignment assignment) throws TransformationException {
|
||||
|
@ -121,7 +124,8 @@ public class Xtext2ECoreInterpretationContext {
|
|||
throws TransformationException {
|
||||
EClassifierInfo featureTypeInfo = eClassifierInfos.getInfo(typeName);
|
||||
if (featureTypeInfo == null) {
|
||||
throw new TransformationException("Cannot resolve type " + typeName, parserElement);
|
||||
throw new TransformationException(ErrorCode.NoSuchTypeAvailable, "Cannot resolve type " + typeName,
|
||||
parserElement);
|
||||
}
|
||||
return featureTypeInfo;
|
||||
}
|
||||
|
@ -132,13 +136,13 @@ public class Xtext2ECoreInterpretationContext {
|
|||
return result;
|
||||
}
|
||||
|
||||
public Xtext2ECoreInterpretationContext spawnContextWith(EClassifierInfo newType) {
|
||||
public Xtext2ECoreInterpretationContext spawnContextWith(EClassifierInfo newType, EObject parserElement)
|
||||
throws TransformationException {
|
||||
if (!isRuleCallAllowed)
|
||||
throw new IllegalStateException("Cannot change type twice within a rule");
|
||||
throw new TransformationException(ErrorCode.MoreThanOneTypeChangeInOneRule,
|
||||
"Cannot change type twice within a rule", parserElement);
|
||||
|
||||
Xtext2ECoreInterpretationContext result = new Xtext2ECoreInterpretationContext(newType, eClassifierInfos,
|
||||
isRuleCallAllowed);
|
||||
return result;
|
||||
return new Xtext2ECoreInterpretationContext(newType, eClassifierInfos, false);
|
||||
}
|
||||
|
||||
public Xtext2ECoreInterpretationContext mergeSpawnedContexts(List<Xtext2ECoreInterpretationContext> contexts) {
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.eclipse.xtext.ParserRule;
|
|||
import org.eclipse.xtext.ReferencedMetamodel;
|
||||
import org.eclipse.xtext.RuleCall;
|
||||
import org.eclipse.xtext.TypeRef;
|
||||
import org.eclipse.xtext.resource.metamodel.ErrorAcceptor.ErrorCode;
|
||||
import org.eclipse.xtext.util.Strings;
|
||||
|
||||
/**
|
||||
|
@ -46,6 +47,21 @@ public class Xtext2EcoreTransformer {
|
|||
private Map<String, EPackage> generatedEPackages;
|
||||
private Grammar superGrammar;
|
||||
private EClassifierInfos eClassifierInfos;
|
||||
private ErrorAcceptor errorAcceptor = new NullErrorAcceptor();
|
||||
|
||||
public ErrorAcceptor getErrorAcceptor() {
|
||||
return errorAcceptor;
|
||||
}
|
||||
|
||||
public void setErrorAcceptor(ErrorAcceptor errorAcceptor) {
|
||||
this.errorAcceptor = errorAcceptor;
|
||||
}
|
||||
|
||||
public class NullErrorAcceptor implements ErrorAcceptor {
|
||||
public void acceptError(ErrorCode errorCode, String arg0, EObject arg1) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
public Xtext2EcoreTransformer() {
|
||||
}
|
||||
|
@ -79,7 +95,7 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
}
|
||||
catch (TransformationException e) {
|
||||
reportError(e.getMessage(), e.getErroneousElement());
|
||||
reportError(e.getErrorCode(), e.getMessage(), e.getErroneousElement());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +109,7 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
}
|
||||
catch (TransformationException e) {
|
||||
reportError(e.getMessage(), e.getErroneousElement());
|
||||
reportError(e.getErrorCode(), e.getMessage(), e.getErroneousElement());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,12 +151,7 @@ public class Xtext2EcoreTransformer {
|
|||
else if (element instanceof RuleCall && !GrammarUtil.isOptionalCardinality(element)) {
|
||||
RuleCall ruleCall = (RuleCall) element;
|
||||
AbstractRule calledRule = GrammarUtil.findRuleForName(grammar, ruleCall.getName());
|
||||
try {
|
||||
return context.spawnContextWith(findOrCreateEClass(calledRule));
|
||||
}
|
||||
catch (IllegalStateException e) {
|
||||
throw new TransformationException(e.getMessage(), ruleCall);
|
||||
}
|
||||
return context.spawnContextWith(findOrCreateEClass(calledRule), ruleCall);
|
||||
}
|
||||
|
||||
return context;
|
||||
|
@ -155,12 +166,15 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
|
||||
private void deriveFeatures(ParserRule rule) throws TransformationException {
|
||||
EClassifierInfo classInfo = findOrCreateEClass(rule);
|
||||
EClassifierInfo classInfo = findEClass(rule);
|
||||
if (classInfo == null)
|
||||
throw new TransformationException(ErrorCode.NoSuchTypeAvailable, "No such type available"
|
||||
+ GrammarUtil.getReturnTypeName(rule), rule);
|
||||
Xtext2ECoreInterpretationContext context = new Xtext2ECoreInterpretationContext(eClassifierInfos, classInfo);
|
||||
deriveFeatures(context, rule.getAlternatives());
|
||||
}
|
||||
|
||||
// TODO : Try to get rid of
|
||||
// TODO : Try to get rid of typref and use qualified name (String) instead
|
||||
private TypeRef getOrFakeReturnType(AbstractRule rule) {
|
||||
TypeRef result = rule.getType();
|
||||
if (result == null) {
|
||||
|
@ -174,10 +188,6 @@ public class Xtext2EcoreTransformer {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alternatives
|
||||
* @throws TransformationException
|
||||
*/
|
||||
private void deriveTypesAndHierarchy(EClassifierInfo ruleReturnType, AbstractElement element)
|
||||
throws TransformationException {
|
||||
if (element instanceof RuleCall) {
|
||||
|
@ -219,14 +229,23 @@ public class Xtext2EcoreTransformer {
|
|||
if (metamodelDeclaration instanceof ReferencedMetamodel) {
|
||||
// load imported metamodel
|
||||
ReferencedMetamodel referencedMetamodel = (ReferencedMetamodel) metamodelDeclaration;
|
||||
EPackage referencedEPackage = GrammarUtil.loadEPackage(referencedMetamodel);
|
||||
EPackage referencedEPackage;
|
||||
try {
|
||||
referencedEPackage = GrammarUtil.loadEPackage(referencedMetamodel);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
referencedEPackage = null;
|
||||
}
|
||||
|
||||
if (referencedEPackage == null) {
|
||||
reportError("Cannot not load metamodel " + referencedMetamodel.getUri(), referencedMetamodel);
|
||||
reportError(ErrorCode.CannotLoadMetamodel, "Cannot not load metamodel "
|
||||
+ referencedMetamodel.getUri(), referencedMetamodel);
|
||||
}
|
||||
else {
|
||||
String alias = referencedMetamodel.getAlias();
|
||||
if (Strings.isEmpty(alias)) {
|
||||
reportError("Referenced metamodels must have an alias", referencedMetamodel);
|
||||
reportError(ErrorCode.MissingAliasForReferencedMetamodel,
|
||||
"Referenced metamodels must have an alias", referencedMetamodel);
|
||||
}
|
||||
else {
|
||||
collectClassInfosOf(referencedEPackage, alias);
|
||||
|
@ -261,17 +280,8 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @param generatedMetamodel
|
||||
*/
|
||||
private void reportError(String message, EObject erroneousElement) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
private void raiseError(String message, EObject erroneousElement) throws TransformationException {
|
||||
throw new TransformationException(message, erroneousElement);
|
||||
private void reportError(ErrorCode errorCode, String message, EObject erroneousElement) {
|
||||
errorAcceptor.acceptError(errorCode, message, erroneousElement);
|
||||
}
|
||||
|
||||
private EClassifierInfo findOrCreateEClass(AbstractRule rule) throws TransformationException {
|
||||
|
@ -279,6 +289,11 @@ public class Xtext2EcoreTransformer {
|
|||
return findOrCreateEClass(typeRef);
|
||||
}
|
||||
|
||||
private EClassifierInfo findEClass(AbstractRule rule) {
|
||||
TypeRef typeRef = getOrFakeReturnType(rule);
|
||||
return eClassifierInfos.getInfo(typeRef);
|
||||
}
|
||||
|
||||
private EClassifierInfo findOrCreateEClass(TypeRef typeRef) throws TransformationException {
|
||||
EClassifierInfo info = eClassifierInfos.getInfo(typeRef);
|
||||
if (info == null) {
|
||||
|
@ -297,7 +312,8 @@ public class Xtext2EcoreTransformer {
|
|||
String typeRefName = typeRef.getName();
|
||||
EPackage generatedEPackage = generatedEPackages.get(typeRefAlias);
|
||||
if (generatedEPackage == null) {
|
||||
raiseError("Cannot create type in alias " + typeRefAlias, typeRef);
|
||||
throw new TransformationException(ErrorCode.CannotCreateTypeInSealedMetamodel,
|
||||
"Cannot create type in alias " + typeRefAlias, typeRef);
|
||||
}
|
||||
EClass generatedEClass = EcoreFactory.eINSTANCE.createEClass();
|
||||
generatedEClass.setName(typeRefName);
|
||||
|
|
|
@ -14,5 +14,6 @@ Require-Bundle: org.eclipse.xtext,
|
|||
org.eclipse.xtext.log4j;bundle-version="1.2.15",
|
||||
org.eclipse.xtend,
|
||||
org.eclipse.xtend.typesystem.emf,
|
||||
org.eclipse.xtend.util.stdlib
|
||||
org.eclipse.xtend.util.stdlib,
|
||||
org.easymock;bundle-version="2.3.0"
|
||||
Export-Package: org.eclipse.xtext
|
||||
|
|
|
@ -7,16 +7,24 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
import static org.easymock.EasyMock.anyObject;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.same;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.EObject;
|
||||
import org.eclipse.emf.ecore.EPackage;
|
||||
import org.eclipse.emf.ecore.EReference;
|
||||
import org.eclipse.xtext.Grammar;
|
||||
import org.eclipse.xtext.XtextStandaloneSetup;
|
||||
import org.eclipse.xtext.resource.metamodel.ErrorAcceptor.ErrorCode;
|
||||
import org.eclipse.xtext.tests.AbstractGeneratorTest;
|
||||
|
||||
/**
|
||||
|
@ -25,17 +33,24 @@ import org.eclipse.xtext.tests.AbstractGeneratorTest;
|
|||
* @see http://wiki.eclipse.org/Xtext/Documentation#Meta-Model_Inference
|
||||
*/
|
||||
public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
||||
private Xtext2EcoreTransformer xtext2EcoreTransformer;
|
||||
private ErrorAcceptor errorAcceptorMock;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
xtext2EcoreTransformer = new Xtext2EcoreTransformer();
|
||||
errorAcceptorMock = createMock(ErrorAcceptor.class);
|
||||
with(XtextStandaloneSetup.class);
|
||||
}
|
||||
|
||||
|
||||
private EPackage getEPackageFromGrammar(String xtextGrammar) throws Exception {
|
||||
Grammar grammar = (Grammar) getModel(xtextGrammar);
|
||||
Xtext2EcoreTransformer xtext2EcoreTransformer = new Xtext2EcoreTransformer();
|
||||
replay(errorAcceptorMock);
|
||||
xtext2EcoreTransformer.setErrorAcceptor(errorAcceptorMock);
|
||||
List<EPackage> metamodels = xtext2EcoreTransformer.transform(grammar);
|
||||
verify(errorAcceptorMock);
|
||||
|
||||
assertNotNull(metamodels);
|
||||
assertEquals(1, metamodels.size());
|
||||
|
||||
|
@ -355,4 +370,28 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
assertReferenceConfiguration(ruleA, 3, "refA4", "TypeB", 0, 1);
|
||||
}
|
||||
|
||||
public void testImportWithoutAlias() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' import 'http://www.eclipse.org/emf/2002/Ecore' RuleA: feature=ID;";
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.MissingAliasForReferencedMetamodel), (String)anyObject(), (EObject)anyObject());
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
|
||||
public void testModifyingSealedModel() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' import 'http://www.eclipse.org/emf/2002/Ecore' as ecore RuleA returns ecore::SomeNewTypeA: feature=ID;";
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.CannotCreateTypeInSealedMetamodel), (String)anyObject(), (EObject)anyObject());
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.NoSuchTypeAvailable), (String)anyObject(), (EObject)anyObject());
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
|
||||
public void testImportingUnknownModel() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' import 'http://www.unknownModel' as unknownModel RuleA: feature=ID;";
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.CannotLoadMetamodel), (String)anyObject(), (EObject)anyObject());
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
|
||||
public void testMoreThanOneRuleCall() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' RuleA: RuleB RuleC; RuleB: featureB=ID; RuleC: featureC=ID;";
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.MoreThanOneTypeChangeInOneRule), (String)anyObject(), (EObject)anyObject());
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue