mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-16 16:58:56 +00:00
Feature uplift, patch by Heiko
see https://bugs.eclipse.org/bugs/attachment.cgi?id=113753
This commit is contained in:
parent
103b86c201
commit
af48e40b1a
5 changed files with 353 additions and 5 deletions
|
@ -94,6 +94,16 @@ 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);
|
||||
}
|
||||
catch (TransformationException e) {
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean addFeature(String featureName, EClassifier featureClassifier, boolean isMultivalue,
|
||||
boolean isContainment, EObject parserElement) throws TransformationException {
|
||||
|
@ -143,7 +153,7 @@ public abstract class EClassifierInfo {
|
|||
}
|
||||
}
|
||||
|
||||
private EClass getEClass() {
|
||||
public EClass getEClass() {
|
||||
return ((EClass) getEClassifier());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.emf.ecore.EClass;
|
||||
|
@ -20,6 +23,7 @@ import org.eclipse.emf.ecore.EDataType;
|
|||
import org.eclipse.xtext.EcoreUtil2;
|
||||
import org.eclipse.xtext.GrammarUtil;
|
||||
import org.eclipse.xtext.TypeRef;
|
||||
import org.eclipse.xtext.resource.metamodel.EClassifierInfo.EClassInfo;
|
||||
|
||||
/**
|
||||
* A possible extension would be to normalize the type hierarchy and remove
|
||||
|
@ -49,9 +53,9 @@ public class EClassifierInfos {
|
|||
return infoMap.get(qualifiedName);
|
||||
}
|
||||
|
||||
private EClassifierInfo getInfo(EClassifier eClassifier) {
|
||||
for(EClassifierInfo info: infoMap.values())
|
||||
if(info.getEClassifier().equals(eClassifier))
|
||||
public EClassifierInfo getInfo(EClassifier eClassifier) {
|
||||
for (EClassifierInfo info : infoMap.values())
|
||||
if (info.getEClassifier().equals(eClassifier))
|
||||
return info;
|
||||
return null;
|
||||
}
|
||||
|
@ -69,7 +73,8 @@ public class EClassifierInfos {
|
|||
"Simple Datatypes (lexer rules or keywords) do not have a common supertype (" + infoA + ", "
|
||||
+ infoB + ")");
|
||||
|
||||
EClassifier compatibleType = EcoreUtil2.getCompatibleType((EClass)infoA.getEClassifier(), (EClass)infoB.getEClassifier());
|
||||
EClassifier compatibleType = EcoreUtil2.getCompatibleType((EClass) infoA.getEClassifier(), (EClass) infoB
|
||||
.getEClassifier());
|
||||
return getInfo(compatibleType);
|
||||
}
|
||||
|
||||
|
@ -94,6 +99,12 @@ public class EClassifierInfos {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EClassInfo getCompatibleTypeOf(Collection<EClassInfo> types) {
|
||||
Collection<EClassifierInfo> infos = (Collection<EClassifierInfo>)(Collection<?>)types;
|
||||
return (EClassInfo)getCompatibleTypeOf(infos);
|
||||
}
|
||||
|
||||
public String getCompatibleTypeNameOf(Collection<String> typeNames) {
|
||||
Collection<EClassifierInfo> types = new HashSet<EClassifierInfo>();
|
||||
|
@ -107,4 +118,20 @@ public class EClassifierInfos {
|
|||
return "ecore::EObject";
|
||||
}
|
||||
|
||||
public List<EClassInfo> getAllEClassInfos() {
|
||||
List<EClassInfo> result = new ArrayList<EClassInfo>();
|
||||
for (EClassifierInfo classifier : this.infoMap.values())
|
||||
if (classifier instanceof EClassInfo)
|
||||
result.add((EClassInfo) classifier);
|
||||
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
public List<EClassInfo> getSuperTypeInfos(EClassInfo subTypeInfo) {
|
||||
List<EClassInfo> result = new ArrayList<EClassInfo>();
|
||||
for (EClass superType : subTypeInfo.getEClass().getESuperTypes())
|
||||
result.add((EClassInfo) this.getInfo(superType));
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2008 itemis AG (http://www.itemis.eu) and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
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.resource.metamodel.EClassifierInfo.EClassInfo;
|
||||
|
||||
/**
|
||||
* @author Heiko Behrens - Initial contribution and API
|
||||
*
|
||||
*/
|
||||
public class TypeHierarchyHelper {
|
||||
private EClassifierInfos infos;
|
||||
private Map<EClassInfo, Set<EClassInfo>> subTypesMap = new HashMap<EClassInfo, Set<EClassInfo>>();
|
||||
private Set<EClassInfo> rootInfos = new HashSet<EClassInfo>();
|
||||
private Set<EClassInfo> traversedTypes = new HashSet<EClassInfo>();
|
||||
|
||||
public TypeHierarchyHelper(EClassifierInfos infos) {
|
||||
super();
|
||||
this.infos = infos;
|
||||
collectTypeData();
|
||||
}
|
||||
|
||||
private void registerSubType(EClassInfo superType, EClassInfo subType) {
|
||||
Set<EClassInfo> subTypes = getSubTypesOf(superType);
|
||||
subTypes.add(subType);
|
||||
}
|
||||
|
||||
private void collectTypeData() {
|
||||
for (EClassInfo classInfo : infos.getAllEClassInfos()) {
|
||||
if (classInfo.getEClass().getESuperTypes().isEmpty())
|
||||
rootInfos.add(classInfo);
|
||||
for (EClassInfo superInfo : infos.getSuperTypeInfos(classInfo))
|
||||
registerSubType(superInfo, classInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public Set<EClassInfo> getSubTypesOf(EClassInfo info) {
|
||||
Set<EClassInfo> result = subTypesMap.get(info);
|
||||
if (result == null) {
|
||||
result = new HashSet<EClassInfo>();
|
||||
subTypesMap.put(info, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void liftUpFeaturesOf(Collection<EClassInfo> infos) {
|
||||
for (EClassInfo info : infos)
|
||||
liftUpFeaturesInto(info);
|
||||
}
|
||||
|
||||
public void liftUpFeaturesInto(EClassInfo superType) {
|
||||
// do not look at types twice (might happen due to multiple inheritance)
|
||||
if (traversedTypes.contains(superType))
|
||||
return;
|
||||
traversedTypes.add(superType);
|
||||
|
||||
Collection<EClassInfo> subTypes = getSubTypesOf(superType);
|
||||
if(subTypes.isEmpty())
|
||||
return;
|
||||
|
||||
// lift up features recursively, deepest first
|
||||
for (EClassInfo subType : subTypes) {
|
||||
liftUpFeaturesInto(subType);
|
||||
}
|
||||
|
||||
// do not modify sealed types
|
||||
if (!superType.isGenerated())
|
||||
return;
|
||||
|
||||
// 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> liftedFeatures = joinFeaturesInto(commonFeatures, superType);
|
||||
for (EClassInfo subClassInfo : subTypes)
|
||||
removeFeatures(subClassInfo, liftedFeatures);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Collection<EStructuralFeature> getCommonFeatures(Collection<EClassInfo> infos) {
|
||||
Collection<EStructuralFeature> result = new HashSet<EStructuralFeature>();
|
||||
|
||||
Iterator<EClassInfo> iterator = infos.iterator();
|
||||
if (iterator.hasNext()) {
|
||||
EClass eClass = iterator.next().getEClass();
|
||||
result.addAll(eClass.getEStructuralFeatures());
|
||||
}
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EList<EStructuralFeature> features = iterator.next().getEClass().getEStructuralFeatures();
|
||||
result = getCommonFeatures(result, features);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Collection<EStructuralFeature> getCommonFeatures(Collection<EStructuralFeature> setA,
|
||||
EList<EStructuralFeature> setB) {
|
||||
Collection<EStructuralFeature> result = new HashSet<EStructuralFeature>();
|
||||
|
||||
for (EStructuralFeature f : setA)
|
||||
if (containsEqualFeature(setB, f))
|
||||
result.add(f);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean containsEqualFeature(Collection<EStructuralFeature> features, EStructuralFeature feature) {
|
||||
for (EStructuralFeature otherFeature : features) {
|
||||
if (isFeatureEqualTo(feature, otherFeature))
|
||||
return true;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
public void liftUpFeatures() {
|
||||
liftUpFeaturesOf(rootInfos);
|
||||
}
|
||||
|
||||
}
|
|
@ -42,6 +42,7 @@ public class AutoTestSuite {
|
|||
suite.addTestSuite(org.eclipse.xtext.reference.CommentOnEofBug_234135_Test.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.reference.LeafNodeBug_234132_Test.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.resource.metamodel.Xtext2EcoreTransformerTests.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.resource.metamodel.TypeHierarchyHelperTests.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.typeresolution.XtextUtilConstructionTest.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.xtext2ecore.ActionTest.class);
|
||||
suite.addTestSuite(org.eclipse.xtext.xtext2ecore.BootstrapModelTest.class);
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
package org.eclipse.xtext.resource.metamodel;
|
||||
|
||||
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.EcoreFactory;
|
||||
import org.eclipse.xtext.resource.metamodel.EClassifierInfo.EClassInfo;
|
||||
|
||||
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 void liftUpFeatures() throws Exception {
|
||||
helper = new TypeHierarchyHelper(infos);
|
||||
helper.liftUpFeatures();
|
||||
}
|
||||
|
||||
private EClassInfo addClass(String name, boolean isGenerated) {
|
||||
EClass eClass = EcoreFactory.eINSTANCE.createEClass();
|
||||
eClass.setName(name);
|
||||
EClassInfo info = (EClassInfo) EClassifierInfo.createEClassInfo(eClass, isGenerated);
|
||||
infos.addInfo("", name, info);
|
||||
return info;
|
||||
}
|
||||
|
||||
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);
|
||||
feature.setEType(eType);
|
||||
eClass.getEClass().getEStructuralFeatures().add(feature);
|
||||
}
|
||||
|
||||
public void testSimpeCase01() 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, INT, "f1");
|
||||
|
||||
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 testSimpeCase02() throws Exception {
|
||||
// no uplift for less than two children
|
||||
EClassInfo a = addClass("a");
|
||||
EClassInfo b = addClass("b");
|
||||
b.addSupertype(a);
|
||||
addAttribute(b, INT, "f1");
|
||||
|
||||
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");
|
||||
EClassInfo b = addClass("b");
|
||||
EClassInfo c = addClass("c");
|
||||
EClassInfo d = addClass("d");
|
||||
EClassInfo e = addClass("e");
|
||||
b.addSupertype(a);
|
||||
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");
|
||||
EClassInfo b = addClass("b");
|
||||
EClassInfo c = addClass("c");
|
||||
EClassInfo d = addClass("d");
|
||||
EClassInfo e = addClass("e");
|
||||
b.addSupertype(a);
|
||||
c.addSupertype(a);
|
||||
d.addSupertype(b);
|
||||
d.addSupertype(c);
|
||||
e.addSupertype(b);
|
||||
e.addSupertype(c);
|
||||
|
||||
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, d.getEClass().getEStructuralFeatures().size());
|
||||
assertEquals(1, e.getEClass().getEStructuralFeatures().size());
|
||||
|
||||
liftUpFeatures();
|
||||
|
||||
assertEquals(0, 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());
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue