blob: 732e15a6bf616bc3a160dae74322e2495861e663 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Obeo.
* 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.acceleo.query.runtime.impl;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.acceleo.query.runtime.IEPackageProvider;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
/**
* {@link EPackageProvider} instances are used to access a set of ecore packages and getting classifiers, and
* enums literals.
*
* @author <a href="mailto:romain.guider@obeo.fr">Romain Guider</a>
*/
public class EPackageProvider implements IEPackageProvider {
/**
* Maps of multi-EOperations : maps the arity to maps that maps {@link EOperation#getName() EOperation's
* name} to their {@link EOperation} {@link List}.
*/
protected final Map<Integer, Map<String, List<EOperation>>> eOperations = new HashMap<Integer, Map<String, List<EOperation>>>();
/**
* Map the name to their corresponding package.
*/
private Multimap<String, EPackage> ePackages = HashMultimap.create();
/**
* {@link Class} to {@link EClassifier} mapping.
*/
private final Map<Class<?>, Set<EClassifier>> class2classifiers = new HashMap<Class<?>, Set<EClassifier>>();
/**
* {@link EClassifier} to {@link Class} mapping.
*/
private final Map<EClassifier, Class<?>> classifier2class = new HashMap<EClassifier, Class<?>>();
/**
* {@link List} of known {@link EOperation} and their addition order.
*/
private final List<EOperation> eOperationsList = new ArrayList<EOperation>();
/**
* Mapping from an {@link EClass} to its containing {@link EStructuralFeature} for one {@link EClass}
* hierarchy.
*/
private final Map<EClass, Set<EStructuralFeature>> containingFeaturesForOneClassHierarchy = new HashMap<EClass, Set<EStructuralFeature>>();
/**
* Mapping from an {@link EClass} to its containing {@link EStructuralFeature}.
*/
private final Map<EClass, Set<EStructuralFeature>> containingFeatures = new HashMap<EClass, Set<EStructuralFeature>>();
/**
* Mapping from an {@link EClass} to all its containing {@link EStructuralFeature} (transitive).
*/
private final Map<EClass, Set<EStructuralFeature>> allContainingFeatures = new HashMap<EClass, Set<EStructuralFeature>>();
/**
* Mapping from an {@link EClass} to its inverse {@link EStructuralFeature}.
*/
private final Map<EClass, Set<EStructuralFeature>> inverseFeatures = new HashMap<EClass, Set<EStructuralFeature>>();
/**
* Mapping from an {@link EClass} to its sub {@link EClass}.
*/
private final Map<EClass, Set<EClass>> subTypes = new HashMap<EClass, Set<EClass>>();
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEPackage(java.lang.String)
*/
@Override
public Collection<EPackage> getEPackage(String name) {
return ePackages.get(name);
}
/**
* Removes the {@link EPackageProvider#registerPackage(EPackage) registered} {@link EPackage}s having the
* given {@link EPackage#getName() name}.
*
* @param name
* the {@link EPackage#getName() name}
* @return the list of removed {@link EPackage}.
*/
public Collection<EPackage> removePackage(String name) {
final Collection<EPackage> removed = ePackages.removeAll(name);
for (EPackage ePackage : removed) {
for (EClassifier eCls : ePackage.getEClassifiers()) {
removeEClassifierClass(eCls);
if (eCls instanceof EClass) {
removeEOperations((EClass)eCls);
removeFeatures((EClass)eCls);
removeSubType((EClass)eCls);
}
}
for (EPackage childPkg : ePackage.getESubpackages()) {
removePackage(childPkg.getName());
}
containingFeatures.clear();
allContainingFeatures.clear();
}
return removed;
}
/**
* Removes the given {@link EClass} as been a sub tpye of its {@link EClass#getESuperTypes() super types}.
*
* @param eCls
* the {@link EClass}
*/
private void removeSubType(EClass eCls) {
for (EClass superECls : eCls.getESuperTypes()) {
Set<EClass> types = subTypes.get(superECls);
if (types != null && types.remove(eCls) && types.size() == 0) {
types = new LinkedHashSet<EClass>();
subTypes.remove(superECls);
}
}
}
/**
* Removes {@link EStructuralFeature} for the given {@link EClass}.
*
* @param eCls
* the {@link EClass} to remove
*/
private void removeFeatures(EClass eCls) {
for (EStructuralFeature feature : eCls.getEStructuralFeatures()) {
if (feature.getEType() instanceof EClass) {
Set<EStructuralFeature> possibleInverseFeatures = inverseFeatures.get(feature.getEType());
if (possibleInverseFeatures != null && possibleInverseFeatures.remove(feature)
&& possibleInverseFeatures.size() == 0) {
inverseFeatures.remove(feature.getEType());
}
if (isContainingEStructuralFeature(feature)) {
Set<EStructuralFeature> possibleContainementFeatures = containingFeaturesForOneClassHierarchy
.get(feature.getEType());
if (possibleContainementFeatures != null && possibleContainementFeatures.remove(feature)
&& possibleContainementFeatures.size() == 0) {
containingFeaturesForOneClassHierarchy.remove(feature.getEType());
}
}
}
}
}
/**
* Tells if the given {@link EStructuralFeature} is a containing {@link EStructuralFeature}.
*
* @param feature
* the {@link EStructuralFeature} to test
* @return <code>true</code> if the given {@link EStructuralFeature} is a containing
* {@link EStructuralFeature},<code>false</code> otherwise
*/
private boolean isContainingEStructuralFeature(EStructuralFeature feature) {
return (feature instanceof EReference && ((EReference)feature).isContainment())
|| feature instanceof EAttribute;
}
/**
* Removes {@link EOperation} of the given {@link EClass}.
*
* @param eCls
* the {@link EClass}
*/
private void removeEOperations(EClass eCls) {
for (EOperation eOperation : eCls.getEOperations()) {
final List<EOperation> multiEOperation = getMultiEOperation(eOperation.getName(), eOperation
.getEParameters().size());
if (multiEOperation != null) {
if (multiEOperation.remove(eOperation) && multiEOperation.size() == 0) {
eOperations.get(eOperation.getEParameters().size()).remove(eOperation.getName());
}
}
eOperationsList.remove(eOperation);
}
}
/**
* Removes the given {@link EClassifier} from {@link EPackageProvider#class2classifiers the class mapping}
* .
*
* @param eCls
* the {@link EClassifier} to remove
*/
private void removeEClassifierClass(EClassifier eCls) {
final Class<?> instanceClass = getClass(eCls);
final Set<EClassifier> classifiers = class2classifiers.get(instanceClass);
if (classifiers != null) {
if (classifiers.size() == 1) {
class2classifiers.remove(instanceClass);
} else {
classifiers.remove(eCls);
}
}
classifier2class.remove(eCls);
}
/**
* Register a new {@link EPackage}.
*
* @param ePackage
* the package to be registered.
* @return the registered {@link EPackage} if any, <code>null</code> otherwise
*/
public EPackage registerPackage(EPackage ePackage) {
final EPackage result;
if (!("ecore".equals(ePackage.getName()) && !EcorePackage.eNS_URI.equals(ePackage.getNsURI()))) {
if (ePackage.getName() != null) {
boolean increasedSize = ePackages.put(ePackage.getName(), ePackage);
if (!increasedSize) {
// duplicate package
return null;
}
result = ePackage;
ePackages.put(ePackage.getName(), ePackage);
for (EClassifier eCls : ePackage.getEClassifiers()) {
registerEClassifierClass(eCls);
if (eCls instanceof EClass) {
registerEOperations((EClass)eCls);
registerFeatures((EClass)eCls);
registerSubTypes((EClass)eCls);
}
}
for (EPackage childPkg : ePackage.getESubpackages()) {
registerPackage(childPkg);
}
containingFeatures.clear();
allContainingFeatures.clear();
} else {
throw new IllegalStateException("Couldn't register package " + ePackage.getName()
+ " because its name is null.");
}
} else {
result = null;
}
return result;
}
/**
* Registers the given {@link EClass} as a sub type of its {@link EClass#getESuperTypes() super types}.
*
* @param eCls
* the {@link EClass}
*/
private void registerSubTypes(EClass eCls) {
for (EClass superECls : eCls.getESuperTypes()) {
Set<EClass> types = subTypes.get(superECls);
if (types == null) {
types = new LinkedHashSet<EClass>();
subTypes.put(superECls, types);
}
types.add(eCls);
}
}
/**
* Registers {@link EStructuralFeature} for the given {@link EClass}.
*
* @param eCls
* the {@link EClass} to register
*/
private void registerFeatures(EClass eCls) {
for (EStructuralFeature feature : eCls.getEStructuralFeatures()) {
if (feature.getEType() instanceof EClass) {
Set<EStructuralFeature> possibleInverseFeatures = inverseFeatures.get(feature.getEType());
if (possibleInverseFeatures == null) {
possibleInverseFeatures = new LinkedHashSet<EStructuralFeature>();
inverseFeatures.put((EClass)feature.getEType(), possibleInverseFeatures);
}
possibleInverseFeatures.add(feature);
if (isContainingEStructuralFeature(feature)) {
Set<EStructuralFeature> possibleContainementFeatures = containingFeaturesForOneClassHierarchy
.get(feature.getEType());
if (possibleContainementFeatures == null) {
possibleContainementFeatures = new LinkedHashSet<EStructuralFeature>();
containingFeaturesForOneClassHierarchy.put((EClass)feature.getEType(),
possibleContainementFeatures);
}
possibleContainementFeatures.add(feature);
}
}
}
}
/**
* Registers the given {@link EClassifier} to {@link EPackageProvider#class2classifiers the class mapping}
* .
*
* @param eCls
* the {@link EClassifier} to register
*/
private void registerEClassifierClass(EClassifier eCls) {
final Class<?> customClass = classifier2class.get(eCls);
final Class<?> instanceClass;
if (customClass != null) {
instanceClass = customClass;
} else {
instanceClass = eCls.getInstanceClass();
classifier2class.put(eCls, instanceClass);
}
Set<EClassifier> classifiers = class2classifiers.get(instanceClass);
if (classifiers == null) {
classifiers = new LinkedHashSet<EClassifier>();
class2classifiers.put(instanceClass, classifiers);
}
classifiers.add(eCls);
}
/**
* Registers {@link EOperation} of the given {@link EClass}.
*
* @param eCls
* the {@link EClass}
*/
private void registerEOperations(EClass eCls) {
for (EOperation eOperation : eCls.getEOperations()) {
final List<EOperation> multiEOperation = getOrCreateMultimethod(eOperation.getName(), eOperation
.getEParameters().size());
multiEOperation.add(eOperation);
eOperationsList.add(eOperation);
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#lookupEOperation(org.eclipse.emf.ecore.EClass,
* java.lang.String, java.util.List)
*/
@Override
public EOperation lookupEOperation(EClass receiverEClass, String eOperationName,
List<EParameter> parameterTypes) {
final EOperation result;
final List<EOperation> multiEOperations = getMultiEOperation(eOperationName, parameterTypes.size());
if (multiEOperations != null) {
EOperation compatibleEOperation = null;
Iterator<EOperation> itOp = multiEOperations.iterator();
while (itOp.hasNext() && compatibleEOperation == null) {
EOperation eOperation = itOp.next();
if (eOperation.getEContainingClass() == EcorePackage.eINSTANCE.getEObject()
|| eOperation.getEContainingClass().isSuperTypeOf(receiverEClass)) {
boolean parametersAreCompatibles = parameterTypes.size() == eOperation.getEParameters()
.size();
final Iterator<EParameter> itParamType = parameterTypes.iterator();
final Iterator<EParameter> itOperationParam = eOperation.getEParameters().iterator();
while (parametersAreCompatibles && itOperationParam.hasNext() && itParamType.hasNext()) {
parametersAreCompatibles = isCompatibleType(itOperationParam.next(), itParamType
.next());
}
if (parametersAreCompatibles) {
compatibleEOperation = eOperation;
}
}
}
result = compatibleEOperation;
} else {
result = null;
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEOperations(java.util.Set)
*/
@Override
public Set<EOperation> getEOperations(Set<EClass> receiverTypes) {
final Set<EOperation> result = new LinkedHashSet<EOperation>();
for (EClass eCls : receiverTypes) {
for (EOperation eOperation : eOperationsList) {
if (eOperation.getEContainingClass().isSuperTypeOf(eCls)) {
result.add(eOperation);
}
}
}
return result;
}
/**
* Tells if the given declared {@link EParameter} is compatible with the given passed {@link Class}.
*
* @param parameter
* the declared {@link EParameter}
* @param passedParameter
* the passed {@link EParameter}
* @return <code>true</code> if the given declared {@link EParameter} is compatible with the given passed
* {@link Class}, <code>false</code> otherwise
*/
private boolean isCompatibleType(EParameter parameter, EParameter passedParameter) {
final boolean result;
if (parameter.isMany() == passedParameter.isMany()) {
if (parameter.getEType() instanceof EClass && passedParameter.getEType() instanceof EClass) {
result = parameter.getEType() == EcorePackage.eINSTANCE.getEObject()
|| ((EClass)parameter.getEType()).isSuperTypeOf((EClass)passedParameter.getEType());
} else if (passedParameter.getEType() != null) {
result = parameter.getEType() == passedParameter.getEType();
} else {
result = true;
}
} else {
result = false;
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getTypes(java.lang.String, java.lang.String)
*/
@Override
public Collection<EClassifier> getTypes(String name, String classifierName) {
Set<EClassifier> classifiers = Sets.newLinkedHashSet();
for (EPackage ePackage : ePackages.get(name)) {
EClassifier clazz = ePackage.getEClassifier(classifierName);
if (clazz != null) {
classifiers.add(clazz);
}
}
return classifiers;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getType(java.lang.String, java.lang.String)
*/
@Override
public EClassifier getType(String name, String classifierName) {
Collection<EClassifier> result = getTypes(name, classifierName);
if (result.size() == 0) {
return null;
} else {
return result.iterator().next();
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEnumLiterals(java.lang.String,
* java.lang.String, java.lang.String)
*/
@Override
public Collection<EEnumLiteral> getEnumLiterals(String name, String enumName, String literalName) {
Collection<EEnumLiteral> result = Sets.newLinkedHashSet();
for (EPackage ePackage : ePackages.get(name)) {
EClassifier eClassifier = ePackage.getEClassifier(enumName);
if (eClassifier != null) {
EEnumLiteral literal = getEnumLiteral(eClassifier, literalName);
if (literal != null) {
result.add(literal);
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getType(java.lang.String)
*/
@Override
public EClassifier getType(String classifierName) {
EClassifier result = null;
for (EPackage ePackage : ePackages.values()) {
EClassifier foundClassifier = ePackage.getEClassifier(classifierName);
if (foundClassifier != null) {
if (result == null) {
result = foundClassifier;
} else {
String firstFullyQualifiedName = result.getEPackage().getName() + "." + result.getName();
String secondFullyQualifiedName = foundClassifier.getEPackage().getName() + "."
+ foundClassifier.getName();
String message = "Ambiguous classifier request. At least two classifiers matches %s : %s and %s";
throw new IllegalStateException(String.format(message, classifierName,
firstFullyQualifiedName, secondFullyQualifiedName));
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEnumLiteral(java.lang.String,
* java.lang.String, java.lang.String)
*/
@Override
public EEnumLiteral getEnumLiteral(String packageName, String enumName, String literalName) {
Collection<EEnumLiteral> result = getEnumLiterals(packageName, enumName, literalName);
if (result.size() == 0) {
return null;
} else {
return result.iterator().next();
}
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEnumLiteral(java.lang.String,
* java.lang.String)
*/
@Override
public EEnumLiteral getEnumLiteral(String enumName, String literalName) {
EClassifier eClassifier = getType(enumName);
if (eClassifier == null) {
return null;
} else {
return getEnumLiteral(eClassifier, literalName);
}
}
/**
* Returns the enum literal that corresponds to the specified literal name in the specified classifier.
*
* @param eClassifier
* the {@link EEnum} in which to look for the literal.
* @param literalName
* the name of the literal to look for.
* @return the enum literal with the specified name in the specified {@link EEnum}.
*/
private EEnumLiteral getEnumLiteral(EClassifier eClassifier, String literalName) {
EEnumLiteral result;
if (eClassifier instanceof EEnum) {
result = ((EEnum)eClassifier).getEEnumLiteral(literalName);
} else {
result = null;
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEClass(java.lang.Class)
*/
@Override
public Set<EClassifier> getEClass(Class<?> cls) {
return class2classifiers.get(cls);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getClass(org.eclipse.emf.ecore.EClassifier)
*/
@Override
public Class<?> getClass(EClassifier eCls) {
return classifier2class.get(eCls);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#isRegistered(org.eclipse.emf.ecore.EClassifier)
*/
public boolean isRegistered(EClassifier eCls) {
return classifier2class.containsKey(eCls);
}
/**
* Registers a custom mapping from an {@link EClassifier} to its {@link Class}.
*
* @param eClassifier
* the {@link EClassifier}
* @param cls
* the {@link Class}
*/
public void registerCustomClassMapping(EClassifier eClassifier, Class<?> cls) {
// remove old mappings
final Class<?> oldClass = classifier2class.remove(eClassifier);
if (oldClass != null) {
final Set<EClassifier> eClassifiers = class2classifiers.get(oldClass);
if (eClassifiers.remove(eClassifier) && eClassifiers.isEmpty()) {
class2classifiers.remove(oldClass);
}
}
// add new mappings
classifier2class.put(eClassifier, cls);
Set<EClassifier> eClassifiers = class2classifiers.get(cls);
if (eClassifiers == null) {
eClassifiers = new LinkedHashSet<EClassifier>();
class2classifiers.put(cls, eClassifiers);
}
eClassifiers.add(eClassifier);
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEStructuralFeatures(java.util.Set)
*/
@Override
public Set<EStructuralFeature> getEStructuralFeatures(Set<EClass> receiverEClasses) {
Set<EStructuralFeature> result = new LinkedHashSet<EStructuralFeature>();
for (EClass eCls : receiverEClasses) {
if (isRegistered(eCls)) {
result.addAll(eCls.getEAllStructuralFeatures());
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEClassifiers()
*/
@Override
public Set<EClassifier> getEClassifiers() {
final Set<EClassifier> result = new LinkedHashSet<EClassifier>();
for (EPackage ePkg : ePackages.values()) {
result.addAll(ePkg.getEClassifiers());
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getEEnumLiterals()
*/
@Override
public Set<EEnumLiteral> getEEnumLiterals() {
final Set<EEnumLiteral> result = new LinkedHashSet<EEnumLiteral>();
for (EPackage ePkg : ePackages.values()) {
for (EClassifier eClassifier : ePkg.getEClassifiers()) {
if (eClassifier instanceof EEnum) {
result.addAll(((EEnum)eClassifier).getELiterals());
}
}
}
return result;
}
/**
* Retrieves a multiEOperation from the given number of {@link EOperation#getEParameters() eParameters}
* and the {@link EOperation#getName() EOperation's name}.
*
* @param eOperationName
* the {@link EOperation#getName() EOperation's name} of the multiEOperation to retrieve.
* @param argc
* the {@link EOperation#getEParameters() eParameters} count of the multiEOperation to
* retrieve.
* @return the list of {@link EOperation} instances that make up the retrieve multiEOperation.
*/
private List<EOperation> getMultiEOperation(String eOperationName, int argc) {
Map<String, List<EOperation>> argcServices = eOperations.get(argc);
if (argcServices == null) {
return null;
} else {
return argcServices.get(eOperationName);
}
}
/**
* Retrieves or creates a multiEOperation from the given number of {@link EOperation#getEParameters()
* eParameters} and the {@link EOperation#getName() EOperation's name}.
*
* @param eOperationName
* the {@link EOperation#getName() EOperation's name} of the multiEOperation to retrieve.
* @param argc
* the {@link EOperation#getEParameters() eParameters} count of the multiEOperation to
* retrieve.
* @return the {@link List} of {@link EOperation} instances that make up the retrieve multiEOperation.
*/
private List<EOperation> getOrCreateMultimethod(String eOperationName, int argc) {
Map<String, List<EOperation>> argcEOperations = eOperations.get(argc);
if (argcEOperations == null) {
argcEOperations = new HashMap<String, List<EOperation>>();
eOperations.put(argc, argcEOperations);
}
List<EOperation> result = argcEOperations.get(eOperationName);
if (result == null) {
result = new ArrayList<EOperation>();
argcEOperations.put(eOperationName, result);
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getContainingEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getContainingEClasses(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
for (EStructuralFeature feature : getContainingEStructuralFeatures(eCls)) {
result.add(feature.getEContainingClass());
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getAllContainingEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getAllContainingEClasses(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
for (EStructuralFeature feature : getAllContainingEStructuralFeatures(eCls)) {
result.add(feature.getEContainingClass());
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider2#getAllContainingEStructuralFeatures(org.eclipse.emf.ecore.EClass)
*/
public Set<EStructuralFeature> getAllContainingEStructuralFeatures(EClass type) {
Set<EStructuralFeature> result = allContainingFeatures.get(type);
if (result == null) {
result = new LinkedHashSet<EStructuralFeature>();
allContainingFeatures.put(type, result);
final Set<EClass> knownECls = new HashSet<EClass>();
Set<EStructuralFeature> previousAdded = new LinkedHashSet<EStructuralFeature>(
getContainingEStructuralFeatures(type));
result.addAll(previousAdded);
while (!previousAdded.isEmpty()) {
Set<EStructuralFeature> currentAdded = new LinkedHashSet<EStructuralFeature>();
for (EStructuralFeature feature : previousAdded) {
final EClass eContainingClass = feature.getEContainingClass();
if (!knownECls.contains(eContainingClass)) {
for (EStructuralFeature parentFeature : getContainingEStructuralFeatures(eContainingClass)) {
if (result.add(parentFeature)) {
knownECls.add(eContainingClass);
currentAdded.add(parentFeature);
}
}
}
}
previousAdded = currentAdded;
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider2#getContainingEStructuralFeatures(org.eclipse.emf.ecore.EClass)
*/
public Set<EStructuralFeature> getContainingEStructuralFeatures(EClass eCls) {
Set<EStructuralFeature> result = containingFeatures.get(eCls);
if (result == null) {
result = new LinkedHashSet<EStructuralFeature>();
containingFeatures.put(eCls, result);
result.addAll(getContainingEStructuralFeaturesForOneEClassHierarchyLevel(eCls));
for (EClass superType : eCls.getEAllSuperTypes()) {
result.addAll(getContainingEStructuralFeaturesForOneEClassHierarchyLevel(superType));
}
for (EClass subType : getAllSubTypes(eCls)) {
result.addAll(getContainingEStructuralFeaturesForOneEClassHierarchyLevel(subType));
}
// always add EObject EClass containing EStructuralFeatures
result.addAll(getContainingEStructuralFeaturesForOneEClassHierarchyLevel(EcorePackage.eINSTANCE
.getEObject()));
}
return result;
}
/**
* Gets the containing {@link EClass} for the given {@link EClass} and only it, not its sub {@link EClass}
* nor super {@link EClass}.
*
* @param eCls
* the {@link EClass}
* @return the containing {@link EClass} for the given {@link EClass} and only it, not its sub
* {@link EClass} nor super {@link EClass}
*/
private Set<EStructuralFeature> getContainingEStructuralFeaturesForOneEClassHierarchyLevel(EClass eCls) {
final Set<EStructuralFeature> result = new LinkedHashSet<EStructuralFeature>();
Set<EStructuralFeature> features = containingFeaturesForOneClassHierarchy.get(eCls);
if (features != null) {
result.addAll(features);
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getAllSubTypes(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getAllSubTypes(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
final Set<EClass> types = subTypes.get(eCls);
if (types != null) {
for (EClass type : types) {
result.add(type);
result.addAll(getAllSubTypes(type));
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getContainedEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getContainedEClasses(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
if (isRegistered(eCls)) {
final Set<EStructuralFeature> features = new LinkedHashSet<EStructuralFeature>(eCls
.getEAllStructuralFeatures());
for (EClass subECls : getAllSubTypes(eCls)) {
features.addAll(subECls.getEStructuralFeatures());
}
for (EStructuralFeature feature : features) {
if (isContainingEStructuralFeature(feature) && feature.getEType() instanceof EClass) {
result.add((EClass)feature.getEType());
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getAllContainedEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getAllContainedEClasses(EClass eCls) {
final Set<EClass> direcltyContainedEClasses = getContainedEClasses(eCls);
final Set<EClass> result = new LinkedHashSet<EClass>(direcltyContainedEClasses);
Set<EClass> added = new LinkedHashSet<EClass>(direcltyContainedEClasses);
while (!added.isEmpty()) {
final Set<EClass> toDig = new LinkedHashSet<EClass>();
for (EClass a : added) {
for (EClass contained : getContainedEClasses(a)) {
if (result.add(contained)) {
toDig.add(contained);
}
}
}
added = toDig;
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getInverseEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getInverseEClasses(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
result.addAll(getInverseClassesForOneEClassHierarchyLevel(eCls));
for (EClass superType : eCls.getEAllSuperTypes()) {
result.addAll(getInverseClassesForOneEClassHierarchyLevel(superType));
}
for (EClass subType : getAllSubTypes(eCls)) {
result.addAll(getInverseClassesForOneEClassHierarchyLevel(subType));
}
// always add EObject EClass containing EClasses
for (EClass eObjectType : getInverseClassesForOneEClassHierarchyLevel(EcorePackage.eINSTANCE
.getEObject())) {
result.add(eObjectType);
}
return result;
}
/**
* Gets inverse {@link EClass} for the given {@link EClass} and only it, not its sub {@link EClass} nor
* super {@link EClass}.
*
* @param eCls
* the {@link EClass}
* @return the containing {@link EClass} for the given {@link EClass} and only it, not its sub
* {@link EClass} nor super {@link EClass}
*/
private Set<EClass> getInverseClassesForOneEClassHierarchyLevel(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
Set<EStructuralFeature> features = inverseFeatures.get(eCls);
if (features != null) {
for (EStructuralFeature feature : features) {
result.add(feature.getEContainingClass());
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getFollowingSiblingsEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getFollowingSiblingsEClasses(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
final Set<EStructuralFeature> containingEStructuralFeatures = getContainingEStructuralFeatures(eCls);
final Set<EStructuralFeature> followingEStructuralFeatures = new LinkedHashSet<EStructuralFeature>();
for (EStructuralFeature feature : containingEStructuralFeatures) {
followingEStructuralFeatures.addAll(getFollowingSiblingEStructuralFeatures(feature));
}
for (EStructuralFeature feature : followingEStructuralFeatures) {
if (isContainingEStructuralFeature(feature) && feature.getEType() instanceof EClass) {
result.add((EClass)feature.getEType());
}
}
return result;
}
/**
* Gets the {@link Set} of following {@link EStructuralFeature} of the given {@link EStructuralFeature},
* if the given {@link EStructuralFeature} is {@link EStructuralFeature#isMany() many} it's included in
* the result.
*
* @param feature
* the {@link EStructuralFeature}
* @return the {@link Set} of following {@link EStructuralFeature} of the given {@link EStructuralFeature}
* , if the given {@link EStructuralFeature} is {@link EStructuralFeature#isMany() many} it's
* included in the result
*/
private Set<EStructuralFeature> getFollowingSiblingEStructuralFeatures(EStructuralFeature feature) {
final Set<EStructuralFeature> result = new LinkedHashSet<EStructuralFeature>();
if (feature.isMany()) {
result.add(feature);
}
boolean add = false;
for (EStructuralFeature childFeature : feature.getEContainingClass().getEStructuralFeatures()) {
if (add && isContainingEStructuralFeature(childFeature)) {
result.add(childFeature);
}
add = add || childFeature == feature;
}
for (EClass eCls : getAllSubTypes(feature.getEContainingClass())) {
for (EStructuralFeature childFeature : eCls.getEStructuralFeatures()) {
if (isContainingEStructuralFeature(childFeature)) {
result.add(childFeature);
}
}
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getPrecedingSiblingsEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getPrecedingSiblingsEClasses(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
final Set<EStructuralFeature> containingEStructuralFeatures = getContainingEStructuralFeatures(eCls);
final Set<EStructuralFeature> precedingEStructuralFeatures = new LinkedHashSet<EStructuralFeature>();
for (EStructuralFeature feature : containingEStructuralFeatures) {
precedingEStructuralFeatures.addAll(getPrecedingSiblingEStructuralFeatures(feature));
}
for (EStructuralFeature feature : precedingEStructuralFeatures) {
if (feature.getEType() instanceof EClass) {
result.add((EClass)feature.getEType());
}
}
return result;
}
/**
* Gets the {@link Set} of preceding {@link EStructuralFeature} of the given {@link EStructuralFeature},
* if the given {@link EStructuralFeature} is {@link EStructuralFeature#isMany() many} it's included in
* the result.
*
* @param feature
* the {@link EStructuralFeature}
* @return the {@link Set} of preceding {@link EStructuralFeature} of the given {@link EStructuralFeature}
* , if the given {@link EStructuralFeature} is {@link EStructuralFeature#isMany() many} it's
* included in the result
*/
private Set<EStructuralFeature> getPrecedingSiblingEStructuralFeatures(EStructuralFeature feature) {
final Set<EStructuralFeature> result = new LinkedHashSet<EStructuralFeature>();
for (EStructuralFeature childFeature : feature.getEContainingClass().getEAllStructuralFeatures()) {
if (childFeature == feature) {
break;
}
if (isContainingEStructuralFeature(childFeature)) {
result.add(childFeature);
}
}
if (feature.isMany()) {
result.add(feature);
}
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getSiblingsEClasses(org.eclipse.emf.ecore.EClass)
*/
@Override
public Set<EClass> getSiblingsEClasses(EClass eCls) {
final Set<EClass> result = new LinkedHashSet<EClass>();
final Set<EStructuralFeature> containingEStructuralFeatures = getContainingEStructuralFeatures(eCls);
final Set<EStructuralFeature> siblingEStructuralFeatures = new LinkedHashSet<EStructuralFeature>();
for (EStructuralFeature feature : containingEStructuralFeatures) {
siblingEStructuralFeatures.addAll(getSiblingEStructuralFeatures(feature));
}
for (EStructuralFeature feature : siblingEStructuralFeatures) {
if (isContainingEStructuralFeature(feature) && feature.getEType() instanceof EClass) {
result.add((EClass)feature.getEType());
}
}
return result;
}
/**
* Gets the {@link Set} of sibling {@link EStructuralFeature} of the given {@link EStructuralFeature}, if
* the given {@link EStructuralFeature} is {@link EStructuralFeature#isMany() many} it's included in the
* result.
*
* @param feature
* the {@link EStructuralFeature}
* @return the {@link Set} of sibling {@link EStructuralFeature} of the given {@link EStructuralFeature},
* if the given {@link EStructuralFeature} is {@link EStructuralFeature#isMany() many} it's
* included in the result
*/
private Set<EStructuralFeature> getSiblingEStructuralFeatures(EStructuralFeature feature) {
final Set<EStructuralFeature> preceding = getPrecedingSiblingEStructuralFeatures(feature);
final Set<EStructuralFeature> following = getFollowingSiblingEStructuralFeatures(feature);
final Set<EStructuralFeature> result = new LinkedHashSet<EStructuralFeature>(preceding.size()
+ following.size());
result.addAll(preceding);
result.addAll(following);
return result;
}
/**
* {@inheritDoc}
*
* @see org.eclipse.acceleo.query.runtime.IEPackageProvider#getRegisteredEPackages()
*/
@Override
public Set<EPackage> getRegisteredEPackages() {
return new LinkedHashSet<EPackage>(ePackages.values());
}
}