mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 16:58:56 +00:00
Applied patch from Heiko:
Feature uplift see https://bugs.eclipse.org/bugs/attachment.cgi?id=113868
This commit is contained in:
parent
af48e40b1a
commit
a14dd5eb00
8 changed files with 362 additions and 161 deletions
|
@ -9,6 +9,7 @@ package org.eclipse.xtext;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
@ -17,10 +18,14 @@ 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.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.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.emf.ecore.EcoreFactory;
|
||||
import org.eclipse.emf.ecore.EcorePackage;
|
||||
import org.eclipse.emf.ecore.resource.Resource;
|
||||
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
|
||||
|
@ -161,16 +166,82 @@ public class EcoreUtil2 extends EcoreUtil {
|
|||
}
|
||||
|
||||
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> result = getCompatibleTypesOf(classA);
|
||||
List<EClass> compatibleTypesOfB = getCompatibleTypesOf(classB);
|
||||
result.retainAll(compatibleTypesOfB);
|
||||
|
||||
List<EClass> intersection = ca;
|
||||
Collections.sort(intersection, new EClassTypeHierarchyComparator());
|
||||
Collections.sort(result, new EClassTypeHierarchyComparator());
|
||||
|
||||
return intersection;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<EClass> getCompatibleTypesOf(EClass eClass) {
|
||||
List<EClass> ca = new ArrayList<EClass>(eClass.getEAllSuperTypes());
|
||||
ca.add(eClass);
|
||||
return ca;
|
||||
}
|
||||
|
||||
public static boolean isFeatureSemanticallyEqualApartFromType(EStructuralFeature f1, EStructuralFeature f2) {
|
||||
boolean result = f1.getName().equals(f1.getName());
|
||||
result &= f1.getLowerBound() == f2.getLowerBound();
|
||||
result &= f1.getUpperBound() == f2.getUpperBound();
|
||||
if (f1 instanceof EReference && f2 instanceof EReference)
|
||||
result &= ((EReference) f1).isContainment() == ((EReference) f2).isContainment();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isFeatureSemanticallyEqualTo(EStructuralFeature f1, EStructuralFeature f2) {
|
||||
boolean result = isFeatureSemanticallyEqualApartFromType(f1, f2);
|
||||
result &= f1.getEType().equals(f2.getEType());
|
||||
return result;
|
||||
}
|
||||
|
||||
public enum FindResult {
|
||||
FeatureDoesNotExist, FeatureExists, DifferentFeatureWithSameNameExists
|
||||
}
|
||||
|
||||
public static EStructuralFeature findFeatureByName(Collection<EStructuralFeature> features, String name) {
|
||||
for (EStructuralFeature feature : features)
|
||||
if(feature.getName().equals(name))
|
||||
return feature;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static FindResult containsSemanticallyEqualFeature(EClass eClass, EStructuralFeature feature) {
|
||||
return containsSemanticallyEqualFeature(eClass.getEAllStructuralFeatures(), feature);
|
||||
}
|
||||
|
||||
public static FindResult containsSemanticallyEqualFeature(Collection<EStructuralFeature> features,
|
||||
EStructuralFeature feature) {
|
||||
EStructuralFeature potentiallyEqualFeature = findFeatureByName(features, feature.getName());
|
||||
if (potentiallyEqualFeature == null)
|
||||
return FindResult.FeatureDoesNotExist;
|
||||
else if (isFeatureSemanticallyEqualTo(potentiallyEqualFeature, feature))
|
||||
return FindResult.FeatureExists;
|
||||
else
|
||||
return FindResult.DifferentFeatureWithSameNameExists;
|
||||
}
|
||||
|
||||
public static EStructuralFeature createFeatureAsCloneOf(EStructuralFeature prototype) {
|
||||
EStructuralFeature result;
|
||||
if (prototype instanceof EReference) {
|
||||
EReference prototypeAsReference = (EReference) prototype;
|
||||
EReference resultAsReference = EcoreFactory.eINSTANCE.createEReference();
|
||||
resultAsReference.setContainment(prototypeAsReference.isContainment());
|
||||
result = resultAsReference;
|
||||
}
|
||||
else if (prototype instanceof EAttribute)
|
||||
result = EcoreFactory.eINSTANCE.createEAttribute();
|
||||
else
|
||||
throw new IllegalArgumentException("Unsupported feature type " + prototype);
|
||||
|
||||
result.setName(prototype.getName());
|
||||
result.setEType(prototype.getEType());
|
||||
result.setLowerBound(prototype.getLowerBound());
|
||||
result.setUpperBound(prototype.getUpperBound());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -68,11 +68,8 @@ public class XtextResource extends ResourceImpl {
|
|||
return parseResult;
|
||||
}
|
||||
|
||||
public void update(int offset, String change) {
|
||||
public void update(int offset, int originalLength, String change) {
|
||||
CompositeNode rootNode = parseResult.getRootNode();
|
||||
int length = change.length();
|
||||
int documentGrowth = length - rootNode.getLength();
|
||||
int originalLength = length - documentGrowth;
|
||||
|
||||
// unloading is required to ensure that any EObjects hanging around
|
||||
// (e.g. in the outline) get a proxied URI
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
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;
|
||||
|
@ -72,7 +71,7 @@ public abstract class EClassifierInfo {
|
|||
}
|
||||
EClass eClass = getEClass();
|
||||
EClass superEClass = (EClass) superTypeInfo.getEClassifier();
|
||||
if(eClass.equals(superEClass))
|
||||
if (eClass.equals(superEClass))
|
||||
// cannot add class as it's own superclass
|
||||
// this usually happens due to a rule call
|
||||
return false;
|
||||
|
@ -80,13 +79,6 @@ public abstract class EClassifierInfo {
|
|||
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,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException {
|
||||
|
@ -94,11 +86,11 @@ public abstract class EClassifierInfo {
|
|||
EClassifier featureClassifier = featureType.getEClassifier();
|
||||
return addFeature(featureName, featureClassifier, isMultivalue, isContainment, parserElement);
|
||||
}
|
||||
|
||||
|
||||
public boolean addFeature(EStructuralFeature prototype) {
|
||||
// TODO implement
|
||||
try {
|
||||
return addFeature(prototype.getName(), prototype.getEType(), prototype.isMany(), false, null);
|
||||
return addFeature(prototype.getName(), prototype.getEType(), prototype.isMany(), prototype
|
||||
.getUpperBound() > 1, null);
|
||||
}
|
||||
catch (TransformationException e) {
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
|
@ -107,6 +99,41 @@ public abstract class EClassifierInfo {
|
|||
|
||||
private boolean addFeature(String featureName, EClassifier featureClassifier, boolean isMultivalue,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException {
|
||||
EStructuralFeature newFeature = createFeatureWith(featureName, featureClassifier, isMultivalue,
|
||||
isContainment);
|
||||
|
||||
switch (EcoreUtil2.containsSemanticallyEqualFeature(getEClass(), newFeature)) {
|
||||
case FeatureDoesNotExist:
|
||||
return getEClass().getEStructuralFeatures().add(newFeature);
|
||||
case FeatureExists:
|
||||
// do nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// feature with same name exists, but have a different, potentially
|
||||
// incompatible configuration
|
||||
EStructuralFeature existingFeature = getEClass().getEStructuralFeature(featureName);
|
||||
|
||||
if (!EcoreUtil2.isFeatureSemanticallyEqualApartFromType(newFeature, existingFeature))
|
||||
throw new TransformationException(ErrorCode.FeatureWithDifferentConfigurationAlreadyExists,
|
||||
"feature with different cardinality or containment configuration already exists " + newFeature,
|
||||
parserElement);
|
||||
|
||||
EClassifier compatibleType = EcoreUtil2
|
||||
.getCompatibleType(existingFeature.getEType(), newFeature.getEType());
|
||||
if (compatibleType == null)
|
||||
throw new TransformationException(ErrorCode.NoCompatibleFeatureTypeAvailable,
|
||||
"Cannot find compatible type for features", parserElement);
|
||||
|
||||
// TODO check, whether existing feature can be changed (feature's
|
||||
// declaring type isGenerated==true)
|
||||
// try to avoid coupling between this class and EClassifierInfos
|
||||
existingFeature.setEType(compatibleType);
|
||||
return true;
|
||||
}
|
||||
|
||||
private EStructuralFeature createFeatureWith(String featureName, EClassifier featureClassifier,
|
||||
boolean isMultivalue, boolean isContainment) {
|
||||
EStructuralFeature newFeature;
|
||||
|
||||
if (featureClassifier instanceof EClass) {
|
||||
|
@ -120,37 +147,7 @@ public abstract class EClassifierInfo {
|
|||
newFeature.setEType(featureClassifier);
|
||||
newFeature.setLowerBound(0);
|
||||
newFeature.setUpperBound(isMultivalue ? -1 : 1);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return newFeature;
|
||||
}
|
||||
|
||||
public EClass getEClass() {
|
||||
|
|
|
@ -16,7 +16,7 @@ import org.eclipse.emf.ecore.EObject;
|
|||
*/
|
||||
public interface ErrorAcceptor {
|
||||
public enum ErrorCode {
|
||||
NoSuchTypeAvailable, MoreThanOneTypeChangeInOneRule, CannotLoadMetamodel, CannotCreateTypeInSealedMetamodel, FeatureWithDifferentConfigurationAlreadyExists, NoCompatibleFeatureTypeAvailable, AliasForMetamodelAlreadyExists
|
||||
NoSuchTypeAvailable, MoreThanOneTypeChangeInOneRule, CannotLoadMetamodel, CannotCreateTypeInSealedMetamodel, FeatureWithDifferentConfigurationAlreadyExists, NoCompatibleFeatureTypeAvailable, AliasForMetamodelAlreadyExists, NoSuchRuleAvailable
|
||||
}
|
||||
|
||||
public void acceptError(ErrorCode errorCode, String message, EObject element);
|
||||
|
|
|
@ -15,9 +15,10 @@ import java.util.Iterator;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.emf.common.util.EList;
|
||||
import org.eclipse.emf.ecore.EClass;
|
||||
import org.eclipse.emf.ecore.EStructuralFeature;
|
||||
import org.eclipse.xtext.EcoreUtil2;
|
||||
import org.eclipse.xtext.EcoreUtil2.FindResult;
|
||||
import org.eclipse.xtext.resource.metamodel.EClassifierInfo.EClassInfo;
|
||||
|
||||
/**
|
||||
|
@ -59,7 +60,8 @@ public class TypeHierarchyHelper {
|
|||
return result;
|
||||
}
|
||||
|
||||
public void liftUpFeaturesOf(Collection<EClassInfo> infos) {
|
||||
public void liftUpFeaturesRecursively(Collection<EClassInfo> infos) {
|
||||
traversedTypes.clear();
|
||||
for (EClassInfo info : infos)
|
||||
liftUpFeaturesInto(info);
|
||||
}
|
||||
|
@ -71,9 +73,9 @@ public class TypeHierarchyHelper {
|
|||
traversedTypes.add(superType);
|
||||
|
||||
Collection<EClassInfo> subTypes = getSubTypesOf(superType);
|
||||
if(subTypes.isEmpty())
|
||||
if (subTypes.isEmpty())
|
||||
return;
|
||||
|
||||
|
||||
// lift up features recursively, deepest first
|
||||
for (EClassInfo subType : subTypes) {
|
||||
liftUpFeaturesInto(subType);
|
||||
|
@ -86,7 +88,7 @@ public class TypeHierarchyHelper {
|
|||
// only if all subtypes' compatible type is superType itself
|
||||
// features can be lifted into superType
|
||||
if (infos.getCompatibleTypeOf(subTypes).equals(superType)) {
|
||||
Collection<EStructuralFeature> commonFeatures = getCommonFeatures(subTypes);
|
||||
Collection<EStructuralFeature> commonFeatures = getCommonDirectFeatures(subTypes);
|
||||
Collection<EStructuralFeature> liftedFeatures = joinFeaturesInto(commonFeatures, superType);
|
||||
for (EClassInfo subClassInfo : subTypes)
|
||||
removeFeatures(subClassInfo, liftedFeatures);
|
||||
|
@ -94,31 +96,30 @@ public class TypeHierarchyHelper {
|
|||
}
|
||||
|
||||
private void removeFeatures(EClassInfo info, Collection<EStructuralFeature> features) {
|
||||
Collection<EStructuralFeature> featuresToBeRemoved = new HashSet<EStructuralFeature>();
|
||||
for (EStructuralFeature feature : info.getEClass().getEStructuralFeatures())
|
||||
if(containsEqualFeature(features, feature))
|
||||
featuresToBeRemoved.add(feature);
|
||||
Collection<EStructuralFeature> featuresToBeModified = info.getEClass().getEStructuralFeatures();
|
||||
for (Iterator<EStructuralFeature> iterator = featuresToBeModified.iterator(); iterator.hasNext();)
|
||||
if (EcoreUtil2.containsSemanticallyEqualFeature(features, iterator.next()) == FindResult.FeatureExists)
|
||||
iterator.remove();
|
||||
|
||||
info.getEClass().getEStructuralFeatures().removeAll(featuresToBeRemoved);
|
||||
}
|
||||
|
||||
private Collection<EStructuralFeature> joinFeaturesInto(Collection<EStructuralFeature> commonFeatures,
|
||||
EClassInfo info) {
|
||||
Collection<EStructuralFeature> result = new HashSet<EStructuralFeature>();
|
||||
for (EStructuralFeature feature : commonFeatures) {
|
||||
if (containsEqualFeature(info.getEClass().getEStructuralFeatures(), feature))
|
||||
// feature already exists, feature can be lifted, nothing to do
|
||||
result.add(feature);
|
||||
else if (info.getEClass().getEStructuralFeature(feature.getName()) == null) {
|
||||
info.addFeature(feature);
|
||||
result.add(feature);
|
||||
switch (EcoreUtil2.containsSemanticallyEqualFeature(info.getEClass(), feature)) {
|
||||
case FeatureDoesNotExist:
|
||||
info.addFeature(feature);
|
||||
case FeatureExists:
|
||||
result.add(feature);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<EStructuralFeature> getCommonFeatures(Collection<EClassInfo> infos) {
|
||||
private Collection<EStructuralFeature> getCommonDirectFeatures(Collection<EClassInfo> infos) {
|
||||
Collection<EStructuralFeature> result = new HashSet<EStructuralFeature>();
|
||||
|
||||
Iterator<EClassInfo> iterator = infos.iterator();
|
||||
|
@ -127,42 +128,54 @@ public class TypeHierarchyHelper {
|
|||
result.addAll(eClass.getEStructuralFeatures());
|
||||
}
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EList<EStructuralFeature> features = iterator.next().getEClass().getEStructuralFeatures();
|
||||
result = getCommonFeatures(result, features);
|
||||
}
|
||||
while (iterator.hasNext())
|
||||
result = getCommonFeatures(iterator.next(), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Collection<EStructuralFeature> getCommonFeatures(Collection<EStructuralFeature> setA,
|
||||
EList<EStructuralFeature> setB) {
|
||||
public Collection<EStructuralFeature> getCommonFeatures(EClassInfo info, Collection<EStructuralFeature> features) {
|
||||
Collection<EStructuralFeature> result = new HashSet<EStructuralFeature>();
|
||||
|
||||
for (EStructuralFeature f : setA)
|
||||
if (containsEqualFeature(setB, f))
|
||||
for (EStructuralFeature f : features)
|
||||
if (EcoreUtil2.containsSemanticallyEqualFeature(info.getEClass(), f) == FindResult.FeatureExists)
|
||||
result.add(f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean containsEqualFeature(Collection<EStructuralFeature> features, EStructuralFeature feature) {
|
||||
for (EStructuralFeature otherFeature : features) {
|
||||
if (isFeatureEqualTo(feature, otherFeature))
|
||||
return true;
|
||||
public void liftUpFeaturesRecursively() {
|
||||
traversedTypes.clear();
|
||||
liftUpFeaturesRecursively(rootInfos);
|
||||
}
|
||||
|
||||
public void removeDuplicateDerivedFeatures() {
|
||||
removeDuplicateDerivedFeaturesOf(infos.getAllEClassInfos());
|
||||
}
|
||||
|
||||
private void removeDuplicateDerivedFeaturesOf(Collection<EClassInfo> classInfos) {
|
||||
for (EClassInfo classInfo : classInfos) {
|
||||
removeDuplicateDerivedFeaturesOf(classInfo);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isFeatureEqualTo(EStructuralFeature f1, EStructuralFeature f2) {
|
||||
// TODO make comparison more precise
|
||||
// TODO extract into a common place, e.g. EcoreUtil2
|
||||
return f1.getName().equals(f2.getName()) && f1.getEType().equals(f2.getEType()) && f1.getLowerBound() == f2.getLowerBound()
|
||||
&& f1.getUpperBound() == f2.getUpperBound();
|
||||
private void removeDuplicateDerivedFeaturesOf(EClassInfo classInfo) {
|
||||
// do not modify sealed types
|
||||
if (!classInfo.isGenerated())
|
||||
return;
|
||||
|
||||
Collection<EStructuralFeature> features = classInfo.getEClass().getEStructuralFeatures();
|
||||
for (Iterator<EStructuralFeature> iterator = features.iterator(); iterator.hasNext();)
|
||||
if(anySuperTypeContainsSemanticallyEqualFeature(classInfo.getEClass(), iterator.next()))
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
public void liftUpFeatures() {
|
||||
liftUpFeaturesOf(rootInfos);
|
||||
private boolean anySuperTypeContainsSemanticallyEqualFeature(EClass eClass, EStructuralFeature feature) {
|
||||
Collection<EStructuralFeature> allSupertypesFeatures = new HashSet<EStructuralFeature>();
|
||||
for (EClass superType : eClass.getEAllSuperTypes())
|
||||
allSupertypesFeatures.addAll(superType.getEAllStructuralFeatures());
|
||||
|
||||
return EcoreUtil2.containsSemanticallyEqualFeature(allSupertypesFeatures, feature) == FindResult.FeatureExists;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -170,7 +170,10 @@ public class Xtext2EcoreTransformer {
|
|||
else if (element instanceof RuleCall && !GrammarUtil.isOptionalCardinality(element)) {
|
||||
RuleCall ruleCall = (RuleCall) element;
|
||||
AbstractRule calledRule = GrammarUtil.findRuleForName(grammar, ruleCall.getName());
|
||||
return context.spawnContextWithCalledRule(findOrCreateEClass(calledRule), ruleCall);
|
||||
// do not throw an exception for missing rules, these have been
|
||||
// announced during the first iteration
|
||||
if (calledRule != null)
|
||||
return context.spawnContextWithCalledRule(findOrCreateEClass(calledRule), ruleCall);
|
||||
}
|
||||
else if (element instanceof Action) {
|
||||
Action action = (Action) element;
|
||||
|
@ -214,14 +217,9 @@ public class Xtext2EcoreTransformer {
|
|||
}
|
||||
|
||||
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
|
||||
TypeHierarchyHelper helper = new TypeHierarchyHelper(this.eClassifierInfos);
|
||||
helper.liftUpFeaturesRecursively();
|
||||
helper.removeDuplicateDerivedFeatures();
|
||||
}
|
||||
|
||||
private void deriveTypesAndHierarchy(EClassifierInfo ruleReturnType, AbstractElement element)
|
||||
|
@ -229,6 +227,9 @@ public class Xtext2EcoreTransformer {
|
|||
if (element instanceof RuleCall) {
|
||||
RuleCall ruleCall = (RuleCall) element;
|
||||
AbstractRule calledRule = GrammarUtil.calledRule(ruleCall);
|
||||
if (calledRule == null)
|
||||
throw new TransformationException(ErrorCode.NoSuchRuleAvailable, "Cannot find rule "
|
||||
+ ruleCall.getName(), ruleCall);
|
||||
TypeRef calledRuleReturnTypeRef = getOrFakeReturnType(calledRule);
|
||||
addSuperType(calledRuleReturnTypeRef, ruleReturnType);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
@ -5,19 +13,24 @@ import junit.framework.TestCase;
|
|||
import org.eclipse.emf.ecore.EAttribute;
|
||||
import org.eclipse.emf.ecore.EClass;
|
||||
import org.eclipse.emf.ecore.EDataType;
|
||||
import org.eclipse.emf.ecore.EReference;
|
||||
import org.eclipse.emf.ecore.EcoreFactory;
|
||||
import org.eclipse.xtext.resource.metamodel.EClassifierInfo.EClassInfo;
|
||||
|
||||
/**
|
||||
* @author Heiko Behrens - Initial contribution and API
|
||||
*
|
||||
*/
|
||||
public class TypeHierarchyHelperTests extends TestCase {
|
||||
|
||||
private TypeHierarchyHelper helper;
|
||||
private EClassifierInfos infos = new EClassifierInfos();
|
||||
private EDataType INT = EcoreFactory.eINSTANCE.createEDataType();
|
||||
// private EDataType STRING = EcoreFactory.eINSTANCE.createEDataType();
|
||||
private EDataType STRING = EcoreFactory.eINSTANCE.createEDataType();
|
||||
|
||||
private void liftUpFeatures() throws Exception {
|
||||
helper = new TypeHierarchyHelper(infos);
|
||||
helper.liftUpFeatures();
|
||||
helper.liftUpFeaturesRecursively();
|
||||
}
|
||||
|
||||
private EClassInfo addClass(String name, boolean isGenerated) {
|
||||
|
@ -31,7 +44,7 @@ public class TypeHierarchyHelperTests extends TestCase {
|
|||
private EClassInfo addClass(String name) {
|
||||
return addClass(name, true);
|
||||
}
|
||||
|
||||
|
||||
private void addAttribute(EClassInfo eClass, EDataType eType, String name) {
|
||||
EAttribute feature = EcoreFactory.eINSTANCE.createEAttribute();
|
||||
feature.setName(name);
|
||||
|
@ -39,6 +52,13 @@ public class TypeHierarchyHelperTests extends TestCase {
|
|||
eClass.getEClass().getEStructuralFeatures().add(feature);
|
||||
}
|
||||
|
||||
private void addReference(EClassInfo eClass, EClassInfo ref, String name) {
|
||||
EReference feature = EcoreFactory.eINSTANCE.createEReference();
|
||||
feature.setName(name);
|
||||
feature.setEType(ref.getEClassifier());
|
||||
eClass.getEClass().getEStructuralFeatures().add(feature);
|
||||
}
|
||||
|
||||
public void testSimpeCase01() throws Exception {
|
||||
EClassInfo a = addClass("a");
|
||||
EClassInfo b = addClass("b");
|
||||
|
@ -51,9 +71,9 @@ public class TypeHierarchyHelperTests extends TestCase {
|
|||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, c.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
|
||||
liftUpFeatures();
|
||||
|
||||
|
||||
assertEquals(1, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, c.getEClass().getEStructuralFeatures().size());
|
||||
|
@ -68,13 +88,13 @@ public class TypeHierarchyHelperTests extends TestCase {
|
|||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
|
||||
liftUpFeatures();
|
||||
|
||||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
}
|
||||
|
||||
|
||||
public void testRecursiveUplift01() throws Exception {
|
||||
// no uplift for less than two children
|
||||
EClassInfo a = addClass("a");
|
||||
|
@ -86,26 +106,26 @@ public class TypeHierarchyHelperTests extends TestCase {
|
|||
c.addSupertype(a);
|
||||
d.addSupertype(c);
|
||||
e.addSupertype(c);
|
||||
|
||||
|
||||
addAttribute(b, INT, "f1");
|
||||
addAttribute(d, INT, "f1");
|
||||
addAttribute(e, INT, "f1");
|
||||
|
||||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, c.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, d.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, e.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
|
||||
liftUpFeatures();
|
||||
|
||||
|
||||
assertEquals(1, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, c.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, d.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, e.getEClass().getEStructuralFeatures().size());
|
||||
}
|
||||
|
||||
|
||||
public void testNikolaus() throws Exception {
|
||||
// no uplift for less than two children
|
||||
EClassInfo a = addClass("a");
|
||||
|
@ -119,24 +139,87 @@ public class TypeHierarchyHelperTests extends TestCase {
|
|||
d.addSupertype(c);
|
||||
e.addSupertype(b);
|
||||
e.addSupertype(c);
|
||||
|
||||
|
||||
addAttribute(b, STRING, "f2");
|
||||
addAttribute(c, STRING, "f2");
|
||||
addAttribute(d, INT, "f1");
|
||||
addAttribute(e, INT, "f1");
|
||||
|
||||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, c.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, c.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, d.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, e.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
|
||||
liftUpFeatures();
|
||||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
assertEquals(1, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, c.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, d.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, e.getEClass().getEStructuralFeatures().size());
|
||||
}
|
||||
|
||||
public void testImcompatipleFeatures() throws Exception {
|
||||
EClassInfo a = addClass("a");
|
||||
EClassInfo b = addClass("b");
|
||||
EClassInfo c = addClass("c");
|
||||
b.addSupertype(a);
|
||||
c.addSupertype(a);
|
||||
addAttribute(b, INT, "f1");
|
||||
addAttribute(c, STRING, "f1");
|
||||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, c.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
liftUpFeatures();
|
||||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, c.getEClass().getEStructuralFeatures().size());
|
||||
}
|
||||
|
||||
public void testReferences() throws Exception {
|
||||
EClassInfo a = addClass("a");
|
||||
EClassInfo b = addClass("b");
|
||||
EClassInfo c = addClass("c");
|
||||
EClassInfo d = addClass("d");
|
||||
b.addSupertype(a);
|
||||
c.addSupertype(a);
|
||||
addReference(b, d, "r1");
|
||||
addReference(c, d, "r1");
|
||||
|
||||
assertEquals(0, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, c.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
liftUpFeatures();
|
||||
|
||||
assertEquals(1, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, c.getEClass().getEStructuralFeatures().size());
|
||||
}
|
||||
|
||||
public void testDublicateDerivedFeature() throws Exception {
|
||||
EClassInfo a = addClass("a");
|
||||
EClassInfo b = addClass("b");
|
||||
EClassInfo c = addClass("b");
|
||||
b.addSupertype(a);
|
||||
c.addSupertype(b);
|
||||
addAttribute(a, INT, "f");
|
||||
addAttribute(c, INT, "f");
|
||||
|
||||
assertEquals(1, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, c.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
helper = new TypeHierarchyHelper(infos);
|
||||
helper.removeDuplicateDerivedFeatures();
|
||||
|
||||
assertEquals(1, a.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, b.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(0, c.getEClass().getEStructuralFeatures().size());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -234,14 +234,13 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
assertEquals(ruleA, ruleC.getESuperTypes().get(0));
|
||||
|
||||
// test all features are separated
|
||||
assertEquals(0, ruleA.getEAttributes().size());
|
||||
assertEquals(2, ruleB.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleB, 0, "featureA", "EString");
|
||||
assertAttributeConfiguration(ruleB, 1, "featureB", "EString");
|
||||
assertEquals(3, ruleC.getEAttributes().size());
|
||||
assertEquals(1, ruleA.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleA, 0, "featureA", "EString");
|
||||
assertEquals(1, ruleB.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleB, 0, "featureB", "EString");
|
||||
assertEquals(2, ruleC.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleC, 0, "featureC1", "EString");
|
||||
assertAttributeConfiguration(ruleC, 1, "featureA", "EString");
|
||||
assertAttributeConfiguration(ruleC, 2, "featureC2", "EString");
|
||||
assertAttributeConfiguration(ruleC, 1, "featureC2", "EString");
|
||||
}
|
||||
|
||||
public void testFeaturesAndInheritanceOfOptionalOptionalRuleCalls() throws Exception {
|
||||
|
@ -273,7 +272,11 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
}
|
||||
|
||||
public void testFeaturesAndInheritanceOfNestedRuleCalls() throws Exception {
|
||||
final String grammar = "language test generate test 'http://test' RuleA: ((RuleB|RuleC featureC1=ID)? featureBC=ID | (RuleC|RuleD featureD1=ID) featureCD=ID) featureA=ID; RuleB: featureB2=ID; RuleC: featureC2=ID; RuleD: featureD2=ID;";
|
||||
String grammar = "language test generate test 'http://test'";
|
||||
grammar += " RuleA: ((RuleB|RuleC featureC1=ID)? featureBC=ID | (RuleC|RuleD featureD1=ID) featureCD=ID) featureA=ID;";
|
||||
grammar += " RuleB: featureB2=ID;";
|
||||
grammar += " RuleC: featureC2=ID;";
|
||||
grammar += " RuleD: featureD2=ID;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
assertEquals(4, ePackage.getEClassifiers().size());
|
||||
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
|
||||
|
@ -300,16 +303,14 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
assertAttributeConfiguration(ruleA, 1, "featureA", "EString");
|
||||
assertEquals(1, ruleB.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleB, 0, "featureB2", "EString");
|
||||
assertEquals(4, ruleC.getEAttributes().size());
|
||||
assertEquals(3, ruleC.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleC, 0, "featureC1", "EString");
|
||||
assertAttributeConfiguration(ruleC, 1, "featureCD", "EString");
|
||||
assertAttributeConfiguration(ruleC, 2, "featureA", "EString");
|
||||
assertAttributeConfiguration(ruleC, 3, "featureC2", "EString");
|
||||
assertEquals(4, ruleD.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleC, 2, "featureC2", "EString");
|
||||
assertEquals(3, ruleD.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleD, 0, "featureD1", "EString");
|
||||
assertAttributeConfiguration(ruleD, 1, "featureCD", "EString");
|
||||
assertAttributeConfiguration(ruleD, 2, "featureA", "EString");
|
||||
assertAttributeConfiguration(ruleD, 3, "featureD2", "EString");
|
||||
assertAttributeConfiguration(ruleD, 2, "featureD2", "EString");
|
||||
}
|
||||
|
||||
public void testFeaturesAndInheritanceOfActions01() throws Exception {
|
||||
|
@ -331,15 +332,16 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
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);
|
||||
assertEquals(1, ruleA.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleA, 0, "featureAS", "EString");
|
||||
assertEquals(1, ruleA.getEReferences().size());
|
||||
assertReferenceConfiguration(ruleA, 0, "a", "RuleA", false, 0, 1);
|
||||
|
||||
assertEquals(0, add.getEAttributes().size());
|
||||
assertEquals(0, add.getEReferences().size());
|
||||
|
||||
assertEquals(0, sub.getEAttributes().size());
|
||||
assertEquals(0, sub.getEReferences().size());
|
||||
}
|
||||
|
||||
public void testFeaturesAndInheritanceOfActions02() throws Exception {
|
||||
|
@ -421,7 +423,7 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
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);
|
||||
|
@ -495,14 +497,13 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
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());
|
||||
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.NoSuchTypeAvailable), (String) anyObject(), (EObject) anyObject());
|
||||
|
||||
List<EPackage> ePackages = getEPackagesFromGrammar(grammar);
|
||||
assertEquals(1, ePackages.size());
|
||||
EPackage t1 = ePackages.get(0);
|
||||
|
@ -557,12 +558,10 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
}
|
||||
|
||||
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;";
|
||||
|
||||
// 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);
|
||||
|
@ -581,7 +580,7 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
grammar += " RuleC: RuleD;";
|
||||
grammar += " RuleD: featureD=ID;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
|
||||
|
||||
assertEquals(3, ePackage.getEClassifiers().size());
|
||||
EClass typeA = (EClass) ePackage.getEClassifier("TypeA");
|
||||
assertNotNull(typeA);
|
||||
|
@ -602,7 +601,7 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
grammar += " RuleC: featureC=INT;";
|
||||
grammar += " RuleD: featureD=ID;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
|
||||
|
||||
assertEquals(3, ePackage.getEClassifiers().size());
|
||||
EClass typeA = (EClass) ePackage.getEClassifier("TypeA");
|
||||
assertNotNull(typeA);
|
||||
|
@ -614,9 +613,49 @@ public class Xtext2EcoreTransformerTests extends AbstractGeneratorTest {
|
|||
assertEquals(2, typeA.getEAllAttributes().size());
|
||||
assertAttributeConfiguration(typeA, 0, "featureA1", "EString");
|
||||
assertAttributeConfiguration(typeA, 1, "featureA4", "EInt");
|
||||
|
||||
|
||||
assertEquals(2, typeA.getEReferences().size());
|
||||
assertReferenceConfiguration(typeA, 0, "featureA2", "EObject", true, 0, 1);
|
||||
assertReferenceConfiguration(typeA, 1, "featureA3", "RuleC", true, 0, 1);
|
||||
}
|
||||
|
||||
public void testUplift01() throws Exception {
|
||||
String grammar = "language test generate test 'http://test'";
|
||||
grammar += " RuleA: (RuleB|RuleC) featureA=ID;";
|
||||
grammar += " RuleB: featureB=INT;";
|
||||
grammar += " RuleC: (featureA=ID)?;";
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
|
||||
assertEquals(3, ePackage.getEClassifiers().size());
|
||||
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
|
||||
assertNotNull(ruleA);
|
||||
EClass ruleB = (EClass) ePackage.getEClassifier("RuleB");
|
||||
assertNotNull(ruleB);
|
||||
EClass ruleC = (EClass) ePackage.getEClassifier("RuleC");
|
||||
assertNotNull(ruleC);
|
||||
|
||||
assertEquals(1, ruleA.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleA, 0, "featureA", "EString");
|
||||
|
||||
assertEquals(1, ruleB.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleB, 0, "featureB", "EInt");
|
||||
|
||||
assertEquals(0, ruleC.getEAttributes().size());
|
||||
}
|
||||
|
||||
public void testCallOfUndeclaredRule() throws Exception {
|
||||
String grammar = "language test generate test 'http://test'";
|
||||
grammar += " RuleA: CallOfUndeclaredRule featureA=ID;";
|
||||
|
||||
errorAcceptorMock.acceptError(same(ErrorCode.NoSuchRuleAvailable), (String) anyObject(),
|
||||
(EObject) anyObject());
|
||||
|
||||
EPackage ePackage = getEPackageFromGrammar(grammar);
|
||||
assertEquals(1, ePackage.getEClassifiers().size());
|
||||
EClass ruleA = (EClass) ePackage.getEClassifier("RuleA");
|
||||
assertNotNull(ruleA);
|
||||
assertEquals(1, ruleA.getEAttributes().size());
|
||||
assertAttributeConfiguration(ruleA, 0, "featureA", "EString");
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue