mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 16:58:56 +00:00
Another patch from Heiko
- Replaces Xtend-Transformer and passes all Tests from AutoTestSuite. - Added EcoreUtil2.GetCommonCompatibleTyp see https://bugs.eclipse.org/bugs/attachment.cgi?id=113708
This commit is contained in:
parent
3db14e20f0
commit
b6ccf855d9
10 changed files with 674 additions and 198 deletions
|
@ -1,13 +1,24 @@
|
|||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
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.EClass;
|
||||
import org.eclipse.emf.ecore.EClassifier;
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.EPackage;
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
|
@ -17,91 +28,148 @@ import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
|
|||
import org.eclipse.xtext.resource.ClassloaderClasspathUriResolver;
|
||||
import org.eclipse.xtext.resource.XtextResourceSet;
|
||||
|
||||
/**
|
||||
* @author Heiko Behrens
|
||||
*/
|
||||
public class EcoreUtil2 extends EcoreUtil {
|
||||
|
||||
private static Logger log = Logger.getLogger(EcoreUtil2.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends EObject> T getContainerOfType(EObject ele, Class<T> type) {
|
||||
if (type.isAssignableFrom(ele.getClass()))
|
||||
return (T) ele;
|
||||
if (ele.eContainer() != null)
|
||||
return getContainerOfType(ele.eContainer(), type);
|
||||
return null;
|
||||
}
|
||||
private static Logger log = Logger.getLogger(EcoreUtil2.class);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends EObject> List<T> getAllContentsOfType(EObject ele, Class<T> type) {
|
||||
List<T> result = new ArrayList<T>();
|
||||
TreeIterator<EObject> allContents = ele.eAllContents();
|
||||
while (allContents.hasNext()) {
|
||||
EObject object = allContents.next();
|
||||
if (type.isAssignableFrom(object.getClass())) {
|
||||
result.add((T) object);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends EObject> T getContainerOfType(EObject ele, Class<T> type) {
|
||||
if (type.isAssignableFrom(ele.getClass()))
|
||||
return (T) ele;
|
||||
if (ele.eContainer() != null)
|
||||
return getContainerOfType(ele.eContainer(), type);
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> typeSelect(List<?> elements, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<T>();
|
||||
for (Object ele : elements) {
|
||||
if (ele != null && clazz.isAssignableFrom(ele.getClass())) {
|
||||
result.add((T) ele);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends EObject> List<T> getAllContentsOfType(EObject ele, Class<T> type) {
|
||||
List<T> result = new ArrayList<T>();
|
||||
TreeIterator<EObject> allContents = ele.eAllContents();
|
||||
while (allContents.hasNext()) {
|
||||
EObject object = allContents.next();
|
||||
if (type.isAssignableFrom(object.getClass())) {
|
||||
result.add((T) object);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<EObject> eAllContentsAsList(EObject ele) {
|
||||
List<EObject> result = new ArrayList<EObject>();
|
||||
TreeIterator<EObject> iterator = ele.eAllContents();
|
||||
while (iterator.hasNext())
|
||||
result.add(iterator.next());
|
||||
return result;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> List<T> typeSelect(List<?> elements, Class<T> clazz) {
|
||||
List<T> result = new ArrayList<T>();
|
||||
for (Object ele : elements) {
|
||||
if (ele != null && clazz.isAssignableFrom(ele.getClass())) {
|
||||
result.add((T) ele);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
public static List<EObject> eAllContentsAsList(EObject ele) {
|
||||
List<EObject> result = new ArrayList<EObject>();
|
||||
TreeIterator<EObject> iterator = ele.eAllContents();
|
||||
while (iterator.hasNext())
|
||||
result.add(iterator.next());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public static final EPackage loadEPackage(String uriAsString, ClassLoader classLoader) {
|
||||
if (EPackage.Registry.INSTANCE.containsKey(uriAsString))
|
||||
return EPackage.Registry.INSTANCE.getEPackage(uriAsString);
|
||||
URI uri = URI.createURI(uriAsString);
|
||||
uri = new ClassloaderClasspathUriResolver().resolve(classLoader, uri);
|
||||
Resource resource = new ResourceSetImpl().getResource(uri, true);
|
||||
for (TreeIterator<EObject> allContents = resource.getAllContents(); allContents.hasNext();) {
|
||||
EObject next = allContents.next();
|
||||
if (next instanceof EPackage) {
|
||||
EPackage ePackage = (EPackage) next;
|
||||
// if (ePackage.getNsURI() != null && ePackage.getNsURI().equals(uriAsString)) {
|
||||
return ePackage;
|
||||
// }
|
||||
}
|
||||
}
|
||||
log.error("Could not load EPackage with nsURI" + uriAsString);
|
||||
return null;
|
||||
}
|
||||
public static final EPackage loadEPackage(String uriAsString, ClassLoader classLoader) {
|
||||
if (EPackage.Registry.INSTANCE.containsKey(uriAsString))
|
||||
return EPackage.Registry.INSTANCE.getEPackage(uriAsString);
|
||||
URI uri = URI.createURI(uriAsString);
|
||||
uri = new ClassloaderClasspathUriResolver().resolve(classLoader, uri);
|
||||
Resource resource = new ResourceSetImpl().getResource(uri, true);
|
||||
for (TreeIterator<EObject> allContents = resource.getAllContents(); allContents.hasNext();) {
|
||||
EObject next = allContents.next();
|
||||
if (next instanceof EPackage) {
|
||||
EPackage ePackage = (EPackage) next;
|
||||
// if (ePackage.getNsURI() != null &&
|
||||
// ePackage.getNsURI().equals(uriAsString)) {
|
||||
return ePackage;
|
||||
// }
|
||||
}
|
||||
}
|
||||
log.error("Could not load EPackage with nsURI" + uriAsString);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void saveEPackage(EPackage ePackage, String path) throws IOException {
|
||||
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("ecore", new XMIResourceFactoryImpl());
|
||||
URI uri = URI.createFileURI(path + "/" + ePackage.getName() + ".ecore");
|
||||
Resource metaModelResource = new ResourceSetImpl().createResource(uri);
|
||||
metaModelResource.getContents().add(ePackage);
|
||||
metaModelResource.save(null);
|
||||
}
|
||||
public static void saveEPackage(EPackage ePackage, String path) throws IOException {
|
||||
Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put("ecore", new XMIResourceFactoryImpl());
|
||||
URI uri = URI.createFileURI(path + "/" + ePackage.getName() + ".ecore");
|
||||
Resource metaModelResource = new ResourceSetImpl().createResource(uri);
|
||||
metaModelResource.getContents().add(ePackage);
|
||||
metaModelResource.save(null);
|
||||
}
|
||||
|
||||
public static String getURIFragment(EObject eObject) {
|
||||
Resource resource = eObject.eResource();
|
||||
String fragment = resource.getURIFragment(eObject);
|
||||
return fragment;
|
||||
}
|
||||
public static String getURIFragment(EObject eObject) {
|
||||
Resource resource = eObject.eResource();
|
||||
String fragment = resource.getURIFragment(eObject);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public static EList<EObject> loadModel(String string, ClassLoader classLoader) {
|
||||
URI uri = URI.createURI(string);
|
||||
XtextResourceSet xtextResourceSet = new XtextResourceSet();
|
||||
xtextResourceSet.setClasspathURIContext(classLoader);
|
||||
XtextResourceSet xtextResourceSet = new XtextResourceSet();
|
||||
xtextResourceSet.setClasspathURIContext(classLoader);
|
||||
Resource resource = xtextResourceSet.getResource(uri, true);
|
||||
return resource.getContents();
|
||||
}
|
||||
|
||||
public static EClassifier getCompatibleType(EClassifier typeA, EClassifier typeB) {
|
||||
if (typeA.equals(typeB))
|
||||
return typeA;
|
||||
// no common type for simple datatypes available
|
||||
if (!(typeA instanceof EClass && typeB instanceof EClass))
|
||||
return null;
|
||||
|
||||
List<EClass> sortedCandidates = getSortedCommonCompatibleTypeCandidates((EClass) typeA, (EClass) typeB);
|
||||
for (EClass candidate : sortedCandidates)
|
||||
if (isCommonCompatibleType(candidate, sortedCandidates))
|
||||
return candidate;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class EClassTypeHierarchyComparator implements Comparator<EClass> {
|
||||
|
||||
public int compare(EClass classA, EClass classB) {
|
||||
if (classA.getEAllSuperTypes().contains(classB))
|
||||
return -1;
|
||||
if (classB.getEAllSuperTypes().contains(classA))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLooslyCompatibleWith(EClass classA, EClass classB) {
|
||||
return classA.equals(classB) || classA.getEAllSuperTypes().contains(classB)
|
||||
|| classB.getEAllSuperTypes().contains(classA);
|
||||
}
|
||||
|
||||
private static boolean isCommonCompatibleType(EClass candidate, List<EClass> candidates) {
|
||||
for (EClass otherCandidate : candidates)
|
||||
if (!isLooslyCompatibleWith(candidate, otherCandidate))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<EClass> getSortedCommonCompatibleTypeCandidates(EClass classA, EClass classB) {
|
||||
List<EClass> ca = new ArrayList<EClass>(classA.getEAllSuperTypes());
|
||||
ca.add(classA);
|
||||
List<EClass> cb = new ArrayList<EClass>(classB.getEAllSuperTypes());
|
||||
cb.add(classB);
|
||||
cb.retainAll(ca);
|
||||
ca.retainAll(cb);
|
||||
|
||||
List<EClass> intersection = ca;
|
||||
Collections.sort(intersection, new EClassTypeHierarchyComparator());
|
||||
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -307,6 +307,10 @@ public class GrammarUtil {
|
|||
public static boolean isMultipleAssignment(Assignment a) {
|
||||
return "+=".equals(a.getOperator());
|
||||
}
|
||||
|
||||
public static boolean isMultipleAssignment(Action a) {
|
||||
return "+=".equals(a.getOperator());
|
||||
}
|
||||
|
||||
public static boolean isOptionalCardinality(AbstractElement e) {
|
||||
return e.getCardinality() != null && (e.getCardinality().equals("?") || e.getCardinality().equals("*"));
|
||||
|
|
|
@ -12,8 +12,12 @@ import org.eclipse.emf.common.util.EList;
|
|||
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.EReference;
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.emf.ecore.EcoreFactory;
|
||||
import org.eclipse.xtext.EcoreUtil2;
|
||||
import org.eclipse.xtext.resource.metamodel.ErrorAcceptor.ErrorCode;
|
||||
|
||||
/**
|
||||
* @author Jan Köhnlein - Initial contribution and API
|
||||
|
@ -49,7 +53,8 @@ public abstract class EClassifierInfo {
|
|||
|
||||
public abstract boolean addSupertype(EClassifierInfo superTypeInfo);
|
||||
|
||||
public abstract boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue);
|
||||
public abstract boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException;
|
||||
|
||||
static class EClassInfo extends EClassifierInfo {
|
||||
|
||||
|
@ -65,19 +70,40 @@ public abstract class EClassifierInfo {
|
|||
if (!(superTypeInfo instanceof EClassInfo)) {
|
||||
throw new IllegalArgumentException("superTypeInfo must represent EClass");
|
||||
}
|
||||
EClass eClass = (EClass) getEClassifier();
|
||||
EClass eClass = getEClass();
|
||||
EClass superEClass = (EClass) superTypeInfo.getEClassifier();
|
||||
return eClass.getESuperTypes().add(superEClass);
|
||||
if(eClass.equals(superEClass))
|
||||
// cannot add class as it's own superclass
|
||||
// this usually happens due to a rule call
|
||||
return false;
|
||||
else
|
||||
return eClass.getESuperTypes().add(superEClass);
|
||||
}
|
||||
|
||||
private EStructuralFeature findFeatureInClassIgnoringSuperclasses(EClass eClass, String featureName) {
|
||||
for (EStructuralFeature feature : eClass.getEStructuralFeatures())
|
||||
if (feature.getName().equals(featureName))
|
||||
return feature;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue) {
|
||||
public boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException {
|
||||
|
||||
EClassifier featureClassifier = featureType.getEClassifier();
|
||||
return addFeature(featureName, featureClassifier, isMultivalue, isContainment, parserElement);
|
||||
}
|
||||
|
||||
private boolean addFeature(String featureName, EClassifier featureClassifier, boolean isMultivalue,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException {
|
||||
EStructuralFeature newFeature;
|
||||
|
||||
if (featureClassifier instanceof EClass)
|
||||
newFeature = EcoreFactory.eINSTANCE.createEReference();
|
||||
if (featureClassifier instanceof EClass) {
|
||||
EReference reference = EcoreFactory.eINSTANCE.createEReference();
|
||||
reference.setContainment(isContainment);
|
||||
newFeature = reference;
|
||||
}
|
||||
else
|
||||
newFeature = EcoreFactory.eINSTANCE.createEAttribute();
|
||||
newFeature.setName(featureName);
|
||||
|
@ -85,8 +111,40 @@ public abstract class EClassifierInfo {
|
|||
newFeature.setLowerBound(0);
|
||||
newFeature.setUpperBound(isMultivalue ? -1 : 1);
|
||||
|
||||
EList<EStructuralFeature> features = ((EClass) getEClassifier()).getEStructuralFeatures();
|
||||
return features.add(newFeature);
|
||||
EList<EStructuralFeature> features = getEClass().getEStructuralFeatures();
|
||||
EStructuralFeature existentFeature = findFeatureInClassIgnoringSuperclasses(getEClass(), featureName);
|
||||
if (existentFeature == null) {
|
||||
// feature does not exist, yet
|
||||
return features.add(newFeature);
|
||||
}
|
||||
else {
|
||||
if (newFeature.getLowerBound() != existentFeature.getLowerBound()
|
||||
|| newFeature.getUpperBound() != existentFeature.getUpperBound())
|
||||
throw new TransformationException(ErrorCode.FeatureWithDifferentConfigurationAlreadyExists,
|
||||
"feature with different cardinality already exists " + newFeature, parserElement);
|
||||
else {
|
||||
// TODO Extract into something more genreral that can be used for uplifting
|
||||
// same name but different type => feature should have
|
||||
// compatible type
|
||||
// remove and add again to overcome problem with reference
|
||||
// vs. attribute
|
||||
EClassifier compatibleType = EcoreUtil2.getCompatibleType(existentFeature.getEType(), newFeature
|
||||
.getEType());
|
||||
if (compatibleType == null)
|
||||
throw new TransformationException(ErrorCode.NoCompatibleFeatureTypeAvailable,
|
||||
"Cannot find compatible type for features", parserElement);
|
||||
int oldIndex = features.indexOf(existentFeature);
|
||||
features.remove(existentFeature);
|
||||
addFeature(featureName, compatibleType, isMultivalue, isContainment, parserElement);
|
||||
existentFeature = findFeatureInClassIgnoringSuperclasses(getEClass(), featureName);
|
||||
features.move(oldIndex, existentFeature);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EClass getEClass() {
|
||||
return ((EClass) getEClassifier());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,7 +160,8 @@ public abstract class EClassifierInfo {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue) {
|
||||
public boolean addFeature(String featureName, EClassifierInfo featureType, boolean isMultivalue,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException {
|
||||
throw new UnsupportedOperationException("Cannot add feature to simple datatype");
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,14 @@ package org.eclipse.xtext.resource.metamodel;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
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.EcoreUtil2;
|
||||
import org.eclipse.xtext.GrammarUtil;
|
||||
import org.eclipse.xtext.TypeRef;
|
||||
|
||||
|
@ -47,48 +49,62 @@ public class EClassifierInfos {
|
|||
return infoMap.get(qualifiedName);
|
||||
}
|
||||
|
||||
private EClassifierInfo getInfo(EClassifier eClassifier) {
|
||||
for(EClassifierInfo info: infoMap.values())
|
||||
if(info.getEClassifier().equals(eClassifier))
|
||||
return info;
|
||||
return null;
|
||||
}
|
||||
|
||||
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 + ")");
|
||||
private EClassifierInfo getCompatibleType(EClassifierInfo infoA, EClassifierInfo infoB) {
|
||||
if (infoA.equals(infoB))
|
||||
return infoA;
|
||||
|
||||
// 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";
|
||||
if (infoA.getEClassifier() instanceof EDataType || infoB.getEClassifier() instanceof EDataType)
|
||||
throw new IllegalArgumentException(
|
||||
"Simple Datatypes (lexer rules or keywords) do not have a common supertype (" + infoA + ", "
|
||||
+ infoB + ")");
|
||||
|
||||
EClassifier compatibleType = EcoreUtil2.getCompatibleType((EClass)infoA.getEClassifier(), (EClass)infoB.getEClassifier());
|
||||
return getInfo(compatibleType);
|
||||
}
|
||||
|
||||
private String getQualifiedNameFor(EClass eClass) {
|
||||
private String getQualifiedNameFor(EClassifierInfo classifierInfo) {
|
||||
// lookup could be improved
|
||||
for (String key : infoMap.keySet()) {
|
||||
EClassifierInfo info = infoMap.get(key);
|
||||
if (info.getEClassifier().equals(eClass))
|
||||
if (info.equals(classifierInfo))
|
||||
return key;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCompatibleTypeNameOf(Collection<String> typeNames) {
|
||||
Iterator<String> i = typeNames.iterator();
|
||||
public EClassifierInfo getCompatibleTypeOf(Collection<EClassifierInfo> types) {
|
||||
Iterator<EClassifierInfo> i = types.iterator();
|
||||
if (!i.hasNext())
|
||||
throw new IllegalArgumentException("Empty set of types cannot have a common super type.");
|
||||
throw new IllegalArgumentException("Empty set of types cannot have a compatible type.");
|
||||
|
||||
String result = i.next();
|
||||
EClassifierInfo result = i.next();
|
||||
while (i.hasNext())
|
||||
result = getCompatibleTypeNameOf(result, i.next());
|
||||
result = getCompatibleType(result, i.next());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getCompatibleTypeNameOf(Collection<String> typeNames) {
|
||||
Collection<EClassifierInfo> types = new HashSet<EClassifierInfo>();
|
||||
for (String typeName : typeNames)
|
||||
types.add(getInfo(typeName));
|
||||
|
||||
EClassifierInfo compatibleType = getCompatibleTypeOf(types);
|
||||
if (compatibleType != null)
|
||||
return getQualifiedNameFor(compatibleType);
|
||||
else
|
||||
return "ecore::EObject";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,12 +16,8 @@ import org.eclipse.emf.ecore.EObject;
|
|||
*/
|
||||
public interface ErrorAcceptor {
|
||||
public enum ErrorCode {
|
||||
NoSuchTypeAvailable,
|
||||
MoreThanOneTypeChangeInOneRule,
|
||||
CannotLoadMetamodel,
|
||||
MissingAliasForReferencedMetamodel,
|
||||
CannotCreateTypeInSealedMetamodel
|
||||
NoSuchTypeAvailable, MoreThanOneTypeChangeInOneRule, CannotLoadMetamodel, CannotCreateTypeInSealedMetamodel, FeatureWithDifferentConfigurationAlreadyExists, NoCompatibleFeatureTypeAvailable, AliasForMetamodelAlreadyExists
|
||||
}
|
||||
|
||||
|
||||
public void acceptError(ErrorCode errorCode, String message, EObject element);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.util.Set;
|
|||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.xtext.AbstractElement;
|
||||
import org.eclipse.xtext.AbstractRule;
|
||||
import org.eclipse.xtext.Action;
|
||||
import org.eclipse.xtext.Alternatives;
|
||||
import org.eclipse.xtext.Assignment;
|
||||
import org.eclipse.xtext.CrossReference;
|
||||
|
@ -62,6 +63,7 @@ public class Xtext2ECoreInterpretationContext {
|
|||
public void addFeature(Assignment assignment) throws TransformationException {
|
||||
String featureName = assignment.getFeature();
|
||||
boolean isMultivalue = GrammarUtil.isMultipleAssignment(assignment);
|
||||
boolean isContainment = true;
|
||||
EClassifierInfo featureTypeInfo;
|
||||
|
||||
if (GrammarUtil.isBooleanAssignment(assignment)) {
|
||||
|
@ -70,11 +72,17 @@ public class Xtext2ECoreInterpretationContext {
|
|||
}
|
||||
else {
|
||||
String featureTypeName = getTerminalTypeName(assignment.getTerminal());
|
||||
isContainment = !(assignment.getTerminal() instanceof CrossReference);
|
||||
featureTypeInfo = getEClassifierInfoOrThrowException(featureTypeName, assignment);
|
||||
}
|
||||
|
||||
addFeature(featureName, featureTypeInfo, isMultivalue, isContainment, assignment);
|
||||
}
|
||||
|
||||
public void addFeature(String featureName, EClassifierInfo featureTypeInfo, boolean isMultivalue,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException {
|
||||
for (EClassifierInfo type : currentTypes)
|
||||
type.addFeature(featureName, featureTypeInfo, isMultivalue);
|
||||
type.addFeature(featureName, featureTypeInfo, isMultivalue, isContainment, parserElement);
|
||||
}
|
||||
|
||||
private String getTerminalTypeName(AbstractElement terminal) {
|
||||
|
@ -87,6 +95,8 @@ public class Xtext2ECoreInterpretationContext {
|
|||
CrossReference crossReference = (CrossReference) terminal;
|
||||
return GrammarUtil.getQualifiedName(crossReference.getType());
|
||||
}
|
||||
else if (terminal instanceof Keyword)
|
||||
return "ecore::EString";
|
||||
else {
|
||||
// terminal is ParenthesizedElement
|
||||
// must be either: alternative of lexer rules and keywords or
|
||||
|
@ -136,7 +146,7 @@ public class Xtext2ECoreInterpretationContext {
|
|||
return result;
|
||||
}
|
||||
|
||||
public Xtext2ECoreInterpretationContext spawnContextWith(EClassifierInfo newType, EObject parserElement)
|
||||
public Xtext2ECoreInterpretationContext spawnContextWithCalledRule(EClassifierInfo newType, EObject parserElement)
|
||||
throws TransformationException {
|
||||
if (!isRuleCallAllowed)
|
||||
throw new TransformationException(ErrorCode.MoreThanOneTypeChangeInOneRule,
|
||||
|
@ -156,4 +166,12 @@ public class Xtext2ECoreInterpretationContext {
|
|||
return result;
|
||||
}
|
||||
|
||||
public EClassifierInfo getCurrentCompatibleType() {
|
||||
return eClassifierInfos.getCompatibleTypeOf(currentTypes);
|
||||
}
|
||||
|
||||
public Xtext2ECoreInterpretationContext spawnContextWithReferencedType(EClassifierInfo referencedType, EObject parserElement) {
|
||||
return new Xtext2ECoreInterpretationContext(referencedType, eClassifierInfos, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,9 +9,13 @@
|
|||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.common.util.EList;
|
||||
import org.eclipse.emf.ecore.EClass;
|
||||
|
@ -66,6 +70,17 @@ public class Xtext2EcoreTransformer {
|
|||
public Xtext2EcoreTransformer() {
|
||||
}
|
||||
|
||||
public static List<EPackage> doTransform(Grammar grammar) throws Exception {
|
||||
Xtext2EcoreTransformer transformer = new Xtext2EcoreTransformer();
|
||||
try {
|
||||
return transformer.transform(grammar);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pre-conditions - ensure non-duplicate aliases - ensure all aliases have
|
||||
* matching metamodel declarations
|
||||
|
@ -73,20 +88,33 @@ public class Xtext2EcoreTransformer {
|
|||
|
||||
public List<EPackage> transform(Grammar grammar) {
|
||||
this.grammar = grammar;
|
||||
eClassifierInfos = new EClassifierInfos();
|
||||
generatedEPackages = new HashMap<String, EPackage>();
|
||||
superGrammar = GrammarUtil.getSuperGrammar(grammar);
|
||||
eClassifierInfos = new EClassifierInfos();
|
||||
|
||||
if (superGrammar != null)
|
||||
collectEClassInfosOfSuperGrammar();
|
||||
collectEPackages();
|
||||
|
||||
// create types:
|
||||
// iterate rules
|
||||
// - typeref in actions
|
||||
// type hierarchy
|
||||
// - actions
|
||||
deriveTypes();
|
||||
deriveFeatures();
|
||||
normalizeGeneratedPackages();
|
||||
|
||||
return getGeneratedPackagesSortedByName();
|
||||
}
|
||||
|
||||
private List<EPackage> getGeneratedPackagesSortedByName() {
|
||||
ArrayList<EPackage> result = new ArrayList<EPackage>(generatedEPackages.values());
|
||||
Collections.sort(result, new Comparator<EPackage>() {
|
||||
public int compare(EPackage o1, EPackage o2) {
|
||||
return o1.getName().compareTo(o2.getName());
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private void deriveTypes() {
|
||||
for (AbstractRule rule : grammar.getRules()) {
|
||||
// - return types (lexer and parser rules)
|
||||
try {
|
||||
EClassifierInfo generatedEClass = findOrCreateEClass(rule);
|
||||
if (rule instanceof ParserRule) {
|
||||
|
@ -95,13 +123,12 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
}
|
||||
catch (TransformationException e) {
|
||||
reportError(e.getErrorCode(), e.getMessage(), e.getErroneousElement());
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create features
|
||||
// iterate rules
|
||||
// - feature in actions
|
||||
private void deriveFeatures() {
|
||||
for (AbstractRule rule : grammar.getRules()) {
|
||||
try {
|
||||
if (rule instanceof ParserRule) {
|
||||
|
@ -109,17 +136,9 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
}
|
||||
catch (TransformationException e) {
|
||||
reportError(e.getErrorCode(), e.getMessage(), e.getErroneousElement());
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// feature normalization
|
||||
// - uplift of common feature to supertype
|
||||
// - removal in subtype if already in supertype
|
||||
// - don't combine features with different EDatatypes
|
||||
fillGeneratedPackages();
|
||||
|
||||
return new ArrayList<EPackage>(generatedEPackages.values());
|
||||
}
|
||||
|
||||
private void collectEClassInfosOfSuperGrammar() {
|
||||
|
@ -151,7 +170,17 @@ public class Xtext2EcoreTransformer {
|
|||
else if (element instanceof RuleCall && !GrammarUtil.isOptionalCardinality(element)) {
|
||||
RuleCall ruleCall = (RuleCall) element;
|
||||
AbstractRule calledRule = GrammarUtil.findRuleForName(grammar, ruleCall.getName());
|
||||
return context.spawnContextWith(findOrCreateEClass(calledRule), ruleCall);
|
||||
return context.spawnContextWithCalledRule(findOrCreateEClass(calledRule), ruleCall);
|
||||
}
|
||||
else if (element instanceof Action) {
|
||||
Action action = (Action) element;
|
||||
TypeRef actionTypeRef = action.getTypeName();
|
||||
EClassifierInfo actionType = findOrCreateEClass(actionTypeRef);
|
||||
EClassifierInfo currentCompatibleType = context.getCurrentCompatibleType();
|
||||
context = context.spawnContextWithReferencedType(actionType, action);
|
||||
context.addFeature(action.getFeature(), currentCompatibleType, GrammarUtil.isMultipleAssignment(action),
|
||||
true, action);
|
||||
return context;
|
||||
}
|
||||
|
||||
return context;
|
||||
|
@ -184,8 +213,15 @@ public class Xtext2EcoreTransformer {
|
|||
return result;
|
||||
}
|
||||
|
||||
private void fillGeneratedPackages() {
|
||||
private void normalizeGeneratedPackages() {
|
||||
// TODO Implement
|
||||
|
||||
// feature normalization
|
||||
// - uplift of common feature to supertype
|
||||
// - removal in subtype if already in supertype
|
||||
// - don't combine features with different EDatatypes
|
||||
|
||||
// NOTE: package dependencies
|
||||
}
|
||||
|
||||
private void deriveTypesAndHierarchy(EClassifierInfo ruleReturnType, AbstractElement element)
|
||||
|
@ -224,43 +260,49 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
|
||||
private void collectEPackages() {
|
||||
// TODO derive alias configuration from supergrammar
|
||||
Set<String> usedAliases = new HashSet<String>();
|
||||
|
||||
EList<AbstractMetamodelDeclaration> metamodelDeclarations = grammar.getMetamodelDeclarations();
|
||||
for (AbstractMetamodelDeclaration metamodelDeclaration : metamodelDeclarations) {
|
||||
if (metamodelDeclaration instanceof ReferencedMetamodel) {
|
||||
// load imported metamodel
|
||||
ReferencedMetamodel referencedMetamodel = (ReferencedMetamodel) metamodelDeclaration;
|
||||
EPackage referencedEPackage;
|
||||
try {
|
||||
referencedEPackage = GrammarUtil.loadEPackage(referencedMetamodel);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
referencedEPackage = null;
|
||||
}
|
||||
|
||||
if (referencedEPackage == null) {
|
||||
reportError(ErrorCode.CannotLoadMetamodel, "Cannot not load metamodel "
|
||||
+ referencedMetamodel.getUri(), referencedMetamodel);
|
||||
}
|
||||
else {
|
||||
String alias = referencedMetamodel.getAlias();
|
||||
if (Strings.isEmpty(alias)) {
|
||||
reportError(ErrorCode.MissingAliasForReferencedMetamodel,
|
||||
"Referenced metamodels must have an alias", referencedMetamodel);
|
||||
try {
|
||||
String alias = Strings.emptyIfNull(metamodelDeclaration.getAlias());
|
||||
|
||||
if (usedAliases.contains(alias))
|
||||
throw new TransformationException(ErrorCode.AliasForMetamodelAlreadyExists, "alias already exists "
|
||||
+ alias, metamodelDeclaration);
|
||||
usedAliases.add(alias);
|
||||
|
||||
if (metamodelDeclaration instanceof ReferencedMetamodel) {
|
||||
// load imported metamodel
|
||||
ReferencedMetamodel referencedMetamodel = (ReferencedMetamodel) metamodelDeclaration;
|
||||
EPackage referencedEPackage;
|
||||
try {
|
||||
referencedEPackage = GrammarUtil.loadEPackage(referencedMetamodel);
|
||||
}
|
||||
else {
|
||||
collectClassInfosOf(referencedEPackage, alias);
|
||||
catch (RuntimeException e) {
|
||||
referencedEPackage = null;
|
||||
}
|
||||
|
||||
if (referencedEPackage == null)
|
||||
throw new TransformationException(ErrorCode.CannotLoadMetamodel, "Cannot not load metamodel "
|
||||
+ referencedMetamodel.getUri(), referencedMetamodel);
|
||||
|
||||
collectClassInfosOf(referencedEPackage, alias);
|
||||
}
|
||||
else if (metamodelDeclaration instanceof GeneratedMetamodel) {
|
||||
// instantiate EPackages for generated metamodel
|
||||
GeneratedMetamodel generatedMetamodel = (GeneratedMetamodel) metamodelDeclaration;
|
||||
EPackage generatedEPackage = EcoreFactory.eINSTANCE.createEPackage();
|
||||
generatedEPackage.setName(generatedMetamodel.getName());
|
||||
generatedEPackage.setNsPrefix(generatedMetamodel.getName());
|
||||
generatedEPackage.setNsURI(generatedMetamodel.getNsURI());
|
||||
generatedEPackages.put(alias, generatedEPackage);
|
||||
}
|
||||
|
||||
}
|
||||
else if (metamodelDeclaration instanceof GeneratedMetamodel) {
|
||||
// instantiate EPackages for generated metamodel
|
||||
GeneratedMetamodel generatedMetamodel = (GeneratedMetamodel) metamodelDeclaration;
|
||||
EPackage generatedEPackage = EcoreFactory.eINSTANCE.createEPackage();
|
||||
generatedEPackage.setName(generatedMetamodel.getName());
|
||||
generatedEPackage.setNsPrefix(generatedMetamodel.getName());
|
||||
generatedEPackage.setNsURI(generatedMetamodel.getNsURI());
|
||||
String alias = Strings.emptyIfNull(generatedMetamodel.getAlias());
|
||||
generatedEPackages.put(alias, generatedEPackage);
|
||||
catch (TransformationException e) {
|
||||
reportError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -284,6 +326,10 @@ public class Xtext2EcoreTransformer {
|
|||
errorAcceptor.acceptError(errorCode, message, erroneousElement);
|
||||
}
|
||||
|
||||
private void reportError(TransformationException exception) {
|
||||
reportError(exception.getErrorCode(), exception.getMessage(), exception.getErroneousElement());
|
||||
}
|
||||
|
||||
private EClassifierInfo findOrCreateEClass(AbstractRule rule) throws TransformationException {
|
||||
TypeRef typeRef = getOrFakeReturnType(rule);
|
||||
return findOrCreateEClass(typeRef);
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.eclipse.emf.ecore.EClass;
|
||||
import org.eclipse.emf.ecore.EcoreFactory;
|
||||
|
||||
/**
|
||||
* @author Heiko Behrens - Initial contribution and API
|
||||
*/
|
||||
public class EcoreUtil2Tests extends TestCase {
|
||||
private EClass createEClass(String name) {
|
||||
EClass result = EcoreFactory.eINSTANCE.createEClass();
|
||||
result.setName(name);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void testCommonCompatibleType01() {
|
||||
EClass a = createEClass("a");
|
||||
EClass b = createEClass("b");
|
||||
EClass c = createEClass("c");
|
||||
EClass d = createEClass("d");
|
||||
EClass e = createEClass("e");
|
||||
EClass f = createEClass("f");
|
||||
|
||||
c.getESuperTypes().add(a);
|
||||
d.getESuperTypes().add(c);
|
||||
d.getESuperTypes().add(b);
|
||||
e.getESuperTypes().add(c);
|
||||
f.getESuperTypes().add(a);
|
||||
f.getESuperTypes().add(b);
|
||||
f.getESuperTypes().add(e);
|
||||
|
||||
assertSame(a, EcoreUtil2.getCompatibleType(a, a));
|
||||
assertSame(null, EcoreUtil2.getCompatibleType(d, f));
|
||||
assertSame(c, EcoreUtil2.getCompatibleType(d, e));
|
||||
assertSame(b, EcoreUtil2.getCompatibleType(b, f));
|
||||
assertSame(null, EcoreUtil2.getCompatibleType(b, c));
|
||||
}
|
||||
|
||||
public void testCommonCompatibleType02() {
|
||||
EClass a = createEClass("a");
|
||||
EClass b = createEClass("b");
|
||||
EClass c = createEClass("c");
|
||||
EClass d = createEClass("d");
|
||||
EClass e = createEClass("e");
|
||||
|
||||
b.getESuperTypes().add(a);
|
||||
c.getESuperTypes().add(a);
|
||||
d.getESuperTypes().add(b);
|
||||
d.getESuperTypes().add(c);
|
||||
e.getESuperTypes().add(b);
|
||||
e.getESuperTypes().add(c);
|
||||
|
||||
assertSame(a, EcoreUtil2.getCompatibleType(a, a));
|
||||
|
||||
assertSame(a, EcoreUtil2.getCompatibleType(b, c));
|
||||
assertSame(b, EcoreUtil2.getCompatibleType(b, d));
|
||||
assertSame(a, EcoreUtil2.getCompatibleType(d, e));
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ public class AutoTestSuite {
|
|||
public static Test suite() {
|
||||
TestSuite suite = new TestSuite("org.eclipse.xtext.generator.tests");
|
||||
suite.addTestSuite(org.eclipse.xtext.XtextGrammarTest.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.EcoreUtil2Tests.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.generator.resource.ResourceTest.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.grammarinheritance.InheritanceTest.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.grammarinheritance.ToEcoreTrafoTest.class);
|
||||
|
|
|
@ -43,22 +43,27 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
errorAcceptorMock = createMock(ErrorAcceptor.class);
|
||||
with(XtextStandaloneSetup.class);
|
||||
}
|
||||
|
||||
|
||||
private EPackage getEPackageFromGrammar(String xtextGrammar) throws Exception {
|
||||
Grammar grammar = (Grammar) getModel(xtextGrammar);
|
||||
replay(errorAcceptorMock);
|
||||
xtext2EcoreTransformer.setErrorAcceptor(errorAcceptorMock);
|
||||
List<EPackage> metamodels = xtext2EcoreTransformer.transform(grammar);
|
||||
verify(errorAcceptorMock);
|
||||
|
||||
assertNotNull(metamodels);
|
||||
List<EPackage> metamodels = getEPackagesFromGrammar(xtextGrammar);
|
||||
assertEquals(1, metamodels.size());
|
||||
|
||||
EPackage result = metamodels.get(0);
|
||||
assertNotNull(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private List<EPackage> getEPackagesFromGrammar(String xtextGrammar) throws Exception {
|
||||
Grammar grammar = (Grammar) getModel(xtextGrammar);
|
||||
replay(errorAcceptorMock);
|
||||
xtext2EcoreTransformer.setErrorAcceptor(errorAcceptorMock);
|
||||
List<EPackage> metamodels = xtext2EcoreTransformer.transform(grammar);
|
||||
verify(errorAcceptorMock);
|
||||
|
||||
assertNotNull(metamodels);
|
||||
return metamodels;
|
||||
}
|
||||
|
||||
private EAttribute assertAttributeConfiguration(EClass eClass, int attributeIndex, String featureName,
|
||||
String featureTypeName) {
|
||||
EAttribute feature = eClass.getEAttributes().get(attributeIndex);
|
||||
|
@ -77,19 +82,19 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
|
||||
return feature;
|
||||
}
|
||||
|
||||
|
||||
private EReference assertReferenceConfiguration(EClass eClass, int referenceIndex, String featureName,
|
||||
String featureTypeName, int lowerBound, int upperBound) {
|
||||
String featureTypeName, boolean isContainment, int lowerBound, int upperBound) {
|
||||
EReference reference = eClass.getEReferences().get(referenceIndex);
|
||||
assertEquals(featureName, reference.getName());
|
||||
assertNotNull(reference.getEType());
|
||||
assertEquals(featureTypeName, reference.getEType().getName());
|
||||
assertEquals(isContainment, reference.isContainment());
|
||||
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;";
|
||||
Grammar grammar = (Grammar) getModel(xtextGrammar);
|
||||
|
@ -307,6 +312,72 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
assertAttributeConfiguration(ruleD, 3, "featureD2", "EString");
|
||||
}
|
||||
|
||||
public void testFeaturesAndInheritanceOfActions01() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' RuleA: ({Add.a=current} '+'|{Sub.a=current} '-') featureAS=ID;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
assertEquals(3, ePackage.getEClassifiers().size());
|
||||
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
|
||||
assertNotNull(ruleA);
|
||||
EClass add = (EClass) ePackage.getEClassifier("Add");
|
||||
assertNotNull(add);
|
||||
EClass sub = (EClass) ePackage.getEClassifier("Sub");
|
||||
assertNotNull(sub);
|
||||
|
||||
// test inheritance
|
||||
assertTrue(ruleA.getESuperTypes().isEmpty());
|
||||
assertEquals(1, add.getESuperTypes().size());
|
||||
assertEquals(ruleA, add.getESuperTypes().get(0));
|
||||
assertEquals(1, sub.getESuperTypes().size());
|
||||
assertEquals(ruleA, sub.getESuperTypes().get(0));
|
||||
|
||||
// test features
|
||||
assertEquals(0, ruleA.getEAttributes().size());
|
||||
assertEquals(1, add.getEAttributes().size());
|
||||
assertAttributeConfiguration(add, 0, "featureAS", "EString");
|
||||
assertEquals(1, add.getEReferences().size());
|
||||
assertReferenceConfiguration(add, 0, "a", "RuleA", true, 0, 1);
|
||||
assertEquals(1, sub.getEAttributes().size());
|
||||
assertAttributeConfiguration(sub, 0, "featureAS", "EString");
|
||||
assertEquals(1, sub.getEReferences().size());
|
||||
assertReferenceConfiguration(sub, 0, "a", "RuleA", true, 0, 1);
|
||||
}
|
||||
|
||||
public void testFeaturesAndInheritanceOfActions02() throws Exception {
|
||||
String grammar = "";
|
||||
grammar += "language org.eclipse.xtext.testlanguages.ActionTestLanguage ";
|
||||
grammar += "generate ActionLang";
|
||||
grammar += " 'http://www.eclipse.org/2008/tmf/xtext/ActionLang'";
|
||||
grammar += "";
|
||||
grammar += " Model:";
|
||||
grammar += " (children+=Element)*;";
|
||||
grammar += "";
|
||||
grammar += " Element returns Type:";
|
||||
grammar += " Item ( { Item.items+=current } items+=Item );";
|
||||
grammar += "";
|
||||
grammar += " Item returns Type:";
|
||||
grammar += " { Thing.content=current } name=ID;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
assertEquals(4, ePackage.getEClassifiers().size());
|
||||
|
||||
EClass model = (EClass) ePackage.getEClassifier("Model");
|
||||
assertNotNull(model);
|
||||
EClass type = (EClass) ePackage.getEClassifier("Type");
|
||||
assertNotNull(type);
|
||||
EClass item = (EClass) ePackage.getEClassifier("Item");
|
||||
assertNotNull(item);
|
||||
EClass thing = (EClass) ePackage.getEClassifier("Thing");
|
||||
assertNotNull(thing);
|
||||
|
||||
// type hierarchy
|
||||
assertEquals(0, model.getESuperTypes().size());
|
||||
assertEquals(0, type.getESuperTypes().size());
|
||||
assertEquals(1, item.getESuperTypes().size());
|
||||
assertSame(type, item.getESuperTypes().get(0));
|
||||
assertEquals(1, thing.getESuperTypes().size());
|
||||
assertSame(type, thing.getESuperTypes().get(0));
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -315,12 +386,12 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
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);
|
||||
assertReferenceConfiguration(ruleA, 0, "callA1", "RuleB", true, 0, 1);
|
||||
assertReferenceConfiguration(ruleA, 1, "callA2", "RuleB", true, 0, -1);
|
||||
assertEquals(1, ruleB.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleB, 0, "featureB", "EString");
|
||||
}
|
||||
|
@ -333,19 +404,24 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
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);
|
||||
assertReferenceConfiguration(ruleA, 0, "refA1", "TypeB", false, 0, 1);
|
||||
assertReferenceConfiguration(ruleA, 1, "refA2", "TypeB", false, 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;";
|
||||
String grammar = " language test generate test 'http://test'";
|
||||
grammar += " RuleA: featureA1?=(RuleB) refA1=(RuleB) refA2=(RuleB|RuleC) refA3+=(RuleB|RuleC|RuleD) refA4=(RuleB|RuleD) featureA2+=('a'|'b');";
|
||||
grammar += " RuleB returns TypeB: RuleC? featureB=ID;";
|
||||
grammar += " RuleC: featureC=ID;";
|
||||
grammar += " RuleD returns TypeB: featureD=ID;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
|
||||
assertEquals(3, ePackage.getEClassifiers().size());
|
||||
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
|
||||
assertNotNull(ruleA);
|
||||
|
@ -357,41 +433,165 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
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);
|
||||
assertReferenceConfiguration(ruleA, 0, "refA1", "TypeB", true, 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);
|
||||
assertReferenceConfiguration(ruleA, 1, "refA2", "TypeB", true, 0, 1);
|
||||
assertReferenceConfiguration(ruleA, 2, "refA3", "TypeB", true, 0, -1);
|
||||
assertReferenceConfiguration(ruleA, 3, "refA4", "TypeB", true, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
public void testAssignedKeyWord() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' RuleA: featureA?=('+'|'-') featureB=('*'|'/');";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
assertEquals(1, ePackage.getEClassifiers().size());
|
||||
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
|
||||
|
||||
assertEquals(2, ruleA.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleA, 0, "featureA", "EBoolean", 0, 1);
|
||||
assertAttributeConfiguration(ruleA, 1, "featureB", "EString", 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());
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.AliasForMetamodelAlreadyExists), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
|
||||
|
||||
public void testGenerateTwoModels() throws Exception {
|
||||
String grammar = "";
|
||||
grammar += " language test";
|
||||
grammar += " generate t1 'http://t1'";
|
||||
grammar += " generate t2 'http://t2' as t2";
|
||||
grammar += " RuleA: featureA=ID;";
|
||||
grammar += " RuleB returns t2::TypeB: featureB=ID;";
|
||||
List<EPackage> ePackages = getEPackagesFromGrammar(grammar);
|
||||
assertEquals(2, ePackages.size());
|
||||
|
||||
EPackage t1 = ePackages.get(0);
|
||||
assertEquals("t1", t1.getName());
|
||||
|
||||
assertEquals(1, t1.getEClassifiers().size());
|
||||
EClassifier ruleA = t1.getEClassifier("RuleA");
|
||||
assertNotNull(ruleA);
|
||||
|
||||
EPackage t2 = ePackages.get(1);
|
||||
assertEquals(1, t2.getEClassifiers().size());
|
||||
assertEquals("t2", t2.getName());
|
||||
EClassifier typeB = t2.getEClassifier("TypeB");
|
||||
assertNotNull(typeB);
|
||||
}
|
||||
|
||||
public void testUseSameModelAlias() throws Exception {
|
||||
String grammar = "";
|
||||
grammar += " language test";
|
||||
grammar += " generate t1 'http://t1' as target";
|
||||
grammar += " generate t2 'http://t2' as target";
|
||||
grammar += " RuleA: featureA=ID;"; // no alias => cannot be created
|
||||
grammar += " RuleB returns target::TypeB: featureB=ID;";
|
||||
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.AliasForMetamodelAlreadyExists), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.CannotCreateTypeInSealedMetamodel), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.NoSuchTypeAvailable), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
|
||||
List<EPackage> ePackages = getEPackagesFromGrammar(grammar);
|
||||
assertEquals(1, ePackages.size());
|
||||
EPackage t1 = ePackages.get(0);
|
||||
assertEquals("t1", t1.getName());
|
||||
assertEquals(1, t1.getEClassifiers().size());
|
||||
EClassifier ruleA = t1.getEClassifier("TypeB");
|
||||
assertNotNull(ruleA);
|
||||
}
|
||||
|
||||
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());
|
||||
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());
|
||||
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());
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.MoreThanOneTypeChangeInOneRule), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
|
||||
public void testRuleCallAndAction() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' RuleA: RuleB {TypeC.B = current}; RuleB: featureB=ID;";
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
|
||||
public void testRuleCallActionAndRuleCall() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' RuleA: RuleB {TypeC.B = current} RuleB; RuleB: featureB=ID;";
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.MoreThanOneTypeChangeInOneRule), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
getEPackageFromGrammar(grammar);
|
||||
}
|
||||
|
||||
public void testAddingFeatureTwice() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' RuleA returns TypeA: featureA=ID; RuleB returns TypeA: featureA=STRING;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
assertEquals(1, ePackage.getEClassifiers().size());
|
||||
EClass typeA = (EClass) ePackage.getEClassifier("TypeA");
|
||||
assertNotNull(typeA);
|
||||
|
||||
assertEquals(1, typeA.getEAttributes().size());
|
||||
assertAttributeConfiguration(typeA, 0, "featureA", "EString");
|
||||
}
|
||||
|
||||
public void testAddingDifferentFeaturesWithSameName01() throws Exception {
|
||||
// simple datatypes do not have a common compatible type
|
||||
final String grammar = "" +
|
||||
" language test generate test 'http://test'" +
|
||||
" RuleA returns TypeA: featureA=ID;" +
|
||||
" RuleB returns TypeA: featureA=INT;";
|
||||
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.NoCompatibleFeatureTypeAvailable), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
assertEquals(1, ePackage.getEClassifiers().size());
|
||||
EClass typeA = (EClass) ePackage.getEClassifier("TypeA");
|
||||
assertNotNull(typeA);
|
||||
|
||||
assertEquals(1, typeA.getEAttributes().size());
|
||||
assertAttributeConfiguration(typeA, 0, "featureA", "EString");
|
||||
}
|
||||
|
||||
public void testAddingDifferentFeaturesWithSameName02() throws Exception {
|
||||
String grammar = "language test generate test 'http://test'";
|
||||
grammar += " RuleA returns TypeA: featureA=RuleD;";
|
||||
grammar += " RuleB returns TypeA: featureA=RuleC;";
|
||||
grammar += " RuleC: RuleD;";
|
||||
grammar += " RuleD: featureD=ID;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
|
||||
assertEquals(3, ePackage.getEClassifiers().size());
|
||||
EClass typeA = (EClass) ePackage.getEClassifier("TypeA");
|
||||
assertNotNull(typeA);
|
||||
EClass ruleC = (EClass) ePackage.getEClassifier("RuleC");
|
||||
assertNotNull(ruleC);
|
||||
EClass ruleD = (EClass) ePackage.getEClassifier("RuleD");
|
||||
assertNotNull(ruleD);
|
||||
|
||||
assertEquals(1, typeA.getEReferences().size());
|
||||
assertReferenceConfiguration(typeA, 0, "featureA", "RuleC", true, 0, 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue