/*
* <copyright>
*
* Copyright (c) 2005-2007 Sven Efftinge 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:
* Sven Efftinge - Initial API and implementation
*
* </copyright>
*/
package org.eclipse.gmf.internal.xpand.expression;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.gmf.internal.xpand.model.XpandDefinition;
import org.eclipse.gmf.internal.xpand.xtend.ast.Extension;
/**
* @author Sven Efftinge
* @author Arno Haase
*/
public class PolymorphicResolver {
public static XpandDefinition filterDefinition(final HashMap<XpandDefinition, List<EClassifier>> resolvedDefs, EClassifier targetType, List<EClassifier> paramTypes) {
final List<EClassifier> allParams = new ArrayList<EClassifier>();
allParams.add(targetType);
allParams.addAll(paramTypes);
final List<XpandDefinition> candidateDefinition = new ArrayList<XpandDefinition>();
for (XpandDefinition def : resolvedDefs.keySet()) {
final List<? extends EClassifier> featureParamTypes = resolvedDefs.get(def);
if ((featureParamTypes.size() == allParams.size())
&& (typesComparator.compare(featureParamTypes, allParams) >= 0)) {
candidateDefinition.add(def);
}
}
final Comparator<XpandDefinition> comparator = new Comparator<XpandDefinition>() {
public int compare(XpandDefinition d1, XpandDefinition d2) {
return typesComparator.compare(resolvedDefs.get(d1), resolvedDefs.get(d2));
}
};
try {
return filterWithComparator(candidateDefinition, comparator);
} catch (IllegalStateException ex) {
throw new RuntimeException("Ambiguous definitions " + candidateDefinition.get(0).toString() + " and "
+ candidateDefinition.get(1).toString() + " for param types " + paramTypes);
}
}
public final static Extension getExtension(final Set<Extension> extensions, final String name, final List<EClassifier> paramTypes) {
final List<Extension> candidateExtensions = new ArrayList<Extension>();
for (Extension ext : extensions) {
if (ext.getName().equals(name)) {
final List<? extends EClassifier> featureParamTypes = ext.getParameterTypes();
if ((featureParamTypes.size() == paramTypes.size())
&& (typesComparator.compare(featureParamTypes, paramTypes) >= 0)) {
candidateExtensions.add(ext);
}
}
}
final Comparator<Extension> extensionComparator = new Comparator<Extension>() {
public int compare(Extension e1, Extension e2) {
return typesComparator.compare(e1.getParameterTypes(), e2.getParameterTypes());
}
};
try {
return filterWithComparator(candidateExtensions, extensionComparator);
} catch (IllegalStateException ex) {
// candidateExtensions was passed by reference, hence, it's already sort
throw new RuntimeException("Ambiguous extensions " + candidateExtensions.get(0).toString() + " and "
+ candidateExtensions.get(1).toString() + " for param types " + paramTypes);
}
}
@SuppressWarnings("unchecked")
public static EOperation filterOperation(List<EOperation> allOperations, String name, EClassifier targetType, List<EClassifier> paramTypes) {
final Map<EOperation, List<EClassifier>> candidates = new HashMap<EOperation, List<EClassifier>>();
ArrayList<EClassifier> expectedParamsWithTarget = new ArrayList(paramTypes.size() + 1);
expectedParamsWithTarget.add(targetType);
expectedParamsWithTarget.addAll(paramTypes);
for (EOperation op : allOperations) {
if (op.getName().equals(name) && (op.getEParameters().size() == paramTypes.size())) {
List<EClassifier> candidateOperationParams = new ArrayList<EClassifier>(paramTypes.size() + 1);
if (op.getEContainingClass() != null) {
candidateOperationParams.add(op.getEContainingClass());
}
List<EParameter> allParams = op.getEParameters();
for (EParameter p : allParams ) {
candidateOperationParams.add(p.getEType());
}
if (typesComparator.compare(candidateOperationParams, op.getEContainingClass() != null ? expectedParamsWithTarget : paramTypes) >= 0) {
candidates.put(op, candidateOperationParams);
}
}
}
final Comparator<EOperation> comparator = new Comparator<EOperation>() {
public int compare(EOperation o1, EOperation o2) {
return typesComparator.compare(candidates.get(o1), candidates.get(o2));
}
};
final List<EOperation> candidates2 = new LinkedList<EOperation>();
try {
candidates2.addAll(candidates.keySet());
return filterWithComparator(candidates2, comparator);
} catch (IllegalStateException ex) {
throw new RuntimeException("Ambiguous definitions " + candidates2.get(0).toString() + " and "
+ candidates2.get(1).toString() + " for param types " + paramTypes, ex);
} }
/**
* @throws IllegalStateException when there are more than one candidates with same priority
*/
private static <T> T filterWithComparator(List<T> candidates, Comparator<T> comparator) throws IllegalStateException {
if (candidates.size() == 1) {
return candidates.get(0);
} else if (candidates.isEmpty()) {
return null;
} else {
// sort features by specialization
Collections.sort(candidates, comparator);
if (comparator.compare(candidates.get(1), candidates.get(0)) > 0) {
return candidates.get(0);
} else {
throw new IllegalStateException();
}
}
}
private final static TypesComparator typesComparator = new TypesComparator();
}