blob: 229dd9a25b03cd616ae7de5c07f8799b9339b6e5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013 RCP Vision (http://www.rcp-vision.com) 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
*
* Contributors:
* Lorenzo Bettini - initial API and implementation
*******************************************************************************/
package org.eclipse.emf.parsley.ui.provider;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.parsley.ecore.FeatureResolver;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
/**
* Provides the list of {@link EStructuralFeature} of an {@link EClass}. The
* default is to return the list of all the features in the EClass, but the
* programmer can customize it (for instance, by returning only a superset, or
* using a different order) on an EClass-based strategy.
*
* The customization can be done redefining {@link #buildMap} or
* {@link #buildStringMap} and adding mappings.
*
* In any case, the computation of features is cached, thus, the list of
* features for a given EClass will always be the same after the first invocation.
*
* @author Lorenzo Bettini - initial API and implementation
*
*/
public class FeaturesProvider {
@Inject
private FeatureResolver featureResolver;
private Map<EClass, List<EStructuralFeature>> eClassFeaturesCache = new HashMap<EClass, List<EStructuralFeature>>();
protected EClassToEStructuralFeatureMap map = null;
protected EClassToEStructuralFeatureAsStringsMap stringMap = null;
public FeatureResolver getFeatureResolver() {
return featureResolver;
}
public static class EClassToEStructuralFeatureMap extends
HashMap<EClass, List<EStructuralFeature>> {
private static final long serialVersionUID = 670116975392207101L;
public void mapTo(EClass eClass, EStructuralFeature... features) {
put(eClass, Lists.newArrayList(features));
}
}
/**
* Returns the list of features of the {@link EClass} of the specified
* {@link EObject}, by calling {@link #getFeatures(EClass)}
* @param eObject
* @return if the eObject is null it returns an empty list
*/
public List<EStructuralFeature> getEObjectFeatures(EObject eObject) {
if (eObject == null) {
return Collections.emptyList();
}
return getFeatures(eObject.eClass());
}
/**
* Returns the list of features of the {@link EClass}, using possible customizations
* @param eClass
* @return if the eClass is null it returns an empty list
*/
public List<EStructuralFeature> getFeatures(EClass eClass) {
if (eClass == null) {
return Collections.emptyList();
}
List<EStructuralFeature> features = eClassFeaturesCache.get(eClass);
if (features != null) {
return features;
}
features = getFromMap(eClass);
if (features != null) {
eClassFeaturesCache.put(eClass, features);
return features;
} else {
features = getFromStringMap(eClass);
if (features != null) {
eClassFeaturesCache.put(eClass, features);
return features;
}
}
// default behavior
features = defaultFeatures(eClass);
eClassFeaturesCache.put(eClass, features);
return defaultFeatures(eClass);
}
protected List<EStructuralFeature> defaultFeatures(EClass eClass) {
EList<EStructuralFeature> eAllStructuralFeatures = eClass.getEAllStructuralFeatures();
Collection<EStructuralFeature> features = Collections2.filter(eAllStructuralFeatures, new Predicate<EStructuralFeature>() {
@Override
public boolean apply(EStructuralFeature feature) {
// derived, unchangeable, container and containment features ignored
return feature.isChangeable()
&& !feature.isDerived()
&& !(feature instanceof EReference && (((EReference) feature)
.isContainment()
// || ((EReference) feature).isContainer()
));
}
});
return new BasicEList<EStructuralFeature>(features);
}
protected List<EStructuralFeature> getFromMap(EClass eClass) {
if (map == null) {
buildMapInternal();
}
return map.get(eClass);
}
protected List<EStructuralFeature> getFromStringMap(EClass eClass) {
if (stringMap == null) {
buildStringMapInternal();
}
List<String> list = stringMap.get(eClass.getInstanceClassName());
if (list == null) {
return null;
}
List<EStructuralFeature> result = new LinkedList<EStructuralFeature>();
for (String featureName : list) {
EStructuralFeature feature = getFeatureResolver().getFeature(eClass, featureName);
if (feature != null) {
result.add(feature);
}
}
return result;
}
private void buildMapInternal() {
buildMap(getMap());
}
public EClassToEStructuralFeatureMap getMap() {
if (map == null) {
map = new EClassToEStructuralFeatureMap();
}
return map;
}
private void buildStringMapInternal() {
buildStringMap(getStringMap());
}
public EClassToEStructuralFeatureAsStringsMap getStringMap() {
if (stringMap == null) {
stringMap = new EClassToEStructuralFeatureAsStringsMap();
}
return stringMap;
}
/**
* Derived classes should redefine this method to map an {@link EClass} to
* {@link EStructuralFeature}s.
*
* @param map
*/
protected void buildMap(EClassToEStructuralFeatureMap map) {
// default implementation is empty
}
/**
* Derived classes should redefine this method to map an {@link EClass}'s
* instanceClassName to {@link EStructuralFeature}s' names; the {@link EClass}'s name
* should be obtained using getInstanceClassName().
*
* @param stringMap
*/
protected void buildStringMap(
EClassToEStructuralFeatureAsStringsMap stringMap) {
// default implementation is empty
}
public void addToStringMap(EClass eClass, String...featuresNames) {
getStringMap().mapTo(eClass.getInstanceClassName(), featuresNames);
}
}