Feature uplift, patch by Heiko

see https://bugs.eclipse.org/bugs/attachment.cgi?id=113753
This commit is contained in:
jkohnlein 2008-09-29 15:43:51 +00:00
parent 103b86c201
commit af48e40b1a
5 changed files with 353 additions and 5 deletions

View file

@ -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());
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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());
}
}