/*****************************************************************************
* Copyright (c) 2011 CEA LIST.
*
*
* 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:
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.alf.validation.typing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.papyrus.uml.alf.alf.InstanceCreationExpression;
import org.eclipse.papyrus.uml.alf.alf.InstanceCreationTupleElement;
import org.eclipse.papyrus.uml.alf.alf.QualifiedNameWithBinding;
import org.eclipse.papyrus.uml.alf.alf.TupleElement;
import org.eclipse.papyrus.uml.alf.scoping.AlfScopeProvider;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.DataType;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.PrimitiveType;
public class SignatureFacadeFactory {
public static SignatureFacadeFactory eInstance = new SignatureFacadeFactory() ;
public SignatureFacade createSignatureFacade(EObject o) {
return new SignatureFacade(o) ;
}
public SignatureFacade createConstructorFacade(InstanceCreationExpression exp) throws Exception {
List<TypeExpression> arguments = new ArrayList<TypeExpression>() ;
Map<String, TypeExpression> argumentsMap = new HashMap<String, TypeExpression>() ;
if (exp.getTuple().getInstanceCreationTupleElement() != null) {
for (InstanceCreationTupleElement tupleElement : exp.getTuple().getInstanceCreationTupleElement()) {
TypeExpression typeOfArgument = new TypeUtils().getTypeOfExpression(tupleElement.getObject()) ;
if (typeOfArgument.getTypeFacade() instanceof ErrorTypeFacade)
throw new TypeInferenceException(typeOfArgument) ;
arguments.add(typeOfArgument) ;
argumentsMap.put(tupleElement.getRole(), typeOfArgument) ;
}
}
// first try to determine if the expression directly refers to a Class or a DataType
TypeFacade cddClassifier = TypeFacadeFactory.eInstance.createVoidFacade(exp.getConstructor()) ;
boolean errorInResolutionOfClassifier = false ;
if (cddClassifier instanceof ErrorTypeFacade) {
errorInResolutionOfClassifier = true ;
}
else {
Classifier referencedType = cddClassifier.extractActualType() ;
if (referencedType instanceof PrimitiveType) {
throw new Exception("Constructor invocations do not apply to primitive types") ;
}
if (referencedType instanceof Enumeration) {
throw new Exception("Constructor invocations do not apply to enumerations") ;
}
if (referencedType.isAbstract()) {
throw new Exception("Abstract classifiers cannot be instantiated") ;
}
// The classifier has been resolved. Must determine if arguments match with possible constructors
if (referencedType instanceof org.eclipse.uml2.uml.Class) {
List<EObject> visibleConstructor = AlfScopeProvider.scopingTool.getVisibleOperationsOrBehaviors(referencedType).resolveByName(referencedType.getName()) ;
if (visibleConstructor.size() > 1) {
// try to match with arguments
// if does not match, raise an exception
List<SignatureFacade> visibleConstructorSignatures = new ArrayList<SignatureFacade>() ;
for (EObject cddConstructor : visibleConstructor) {
SignatureFacade cddConstructorSignature = SignatureFacadeFactory.eInstance.createSignatureFacade(cddConstructor) ;
if (cddConstructorSignature.isAConstructor())
visibleConstructorSignatures.add(cddConstructorSignature) ;
}
List<SignatureFacade> matchingSignatures = SignatureFacade.findNearestSignature(arguments, visibleConstructorSignatures) ;
if (matchingSignatures.size() > 1) {
String errorMessage = referencedType.getName() + "(" ;
boolean first = true ;
for (TypeExpression arg : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += arg.getLabel() ;
}
errorMessage += ") resolves to multiple constructors" ;
throw new Exception(errorMessage) ;
}
else if (matchingSignatures.size() == 0) {
String errorMessage = "Constructor " + referencedType.getName() + "(" ;
boolean first = true ;
for (TypeExpression arg : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += arg.getLabel() ;
}
errorMessage += ") is undefined" ;
throw new Exception(errorMessage) ;
}
else { // exactly one match
return matchingSignatures.get(0) ;
}
}
else if (visibleConstructor.size() == 0) {
if (arguments.size() > 0) {
// Throw an exception
String errorMessage = "Constructor " + referencedType.getName() + "(";
boolean first = true ;
for (TypeExpression t : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += t.getLabel() ;
}
errorMessage += ") is undefined" ;
throw new Exception(errorMessage) ;
}
return new DefaultConstructorFacade((Class)referencedType) ;
}
else { // exactly one constructor found
// Tries to determine if arguments match
SignatureFacade constructor = createSignatureFacade(visibleConstructor.get(0)) ;
if (!constructor.isAConstructor()) {
// Throw an exception
String errorMessage = "Constructor " + referencedType.getName() + "(";
boolean first = true ;
for (TypeExpression t : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += t.getLabel() ;
}
errorMessage += ") is undefined" ;
throw new Exception(errorMessage) ;
}
String potentialErrorMessage = constructor.isCompatibleWithMe(argumentsMap) ;
if (potentialErrorMessage.length() == 0)
return constructor ;
else
throw new Exception(potentialErrorMessage) ;
}
}
else if (referencedType instanceof DataType){ // This is a data type.
//must match arguments with visible properties of the data type
SignatureFacade defaultDataTypeConstructor = new DefaultConstructorFacade((DataType)referencedType) ;
String errorMessage = defaultDataTypeConstructor.isCompatibleWithMe(argumentsMap) ;
if (!(errorMessage.length() == 0))
throw new Exception(errorMessage) ;
else
return defaultDataTypeConstructor ;
}
}
if (errorInResolutionOfClassifier) {
// We can try again, but considering that:
// - the last element in the qualified name as the name of a constructor
// - the element before the last element is a class name
if (exp.getConstructor().getRemaining() == null)
throw new Exception("Constructor " + exp.getConstructor().getId() + " is undefined") ;
QualifiedNameWithBinding remaining = exp.getConstructor() ;
QualifiedNameWithBinding cddClassName = exp.getConstructor() ;
QualifiedNameWithBinding cddConstructorName = exp.getConstructor() ;
EObject previousPackage = null ;
while (cddConstructorName.getRemaining() != null) {
cddClassName = cddConstructorName ;
cddConstructorName = cddConstructorName.getRemaining() ;
}
if (remaining != cddClassName) {
List<EObject> visiblePackages = AlfScopeProvider.scopingTool.getVisiblePackages(exp).resolveByName(remaining.getId()) ;
if (visiblePackages.isEmpty()) {
throw new Exception("Could not resolve package " + remaining.getId()) ;
}
else if (visiblePackages.size() > 1) {
throw new Exception(remaining.getId() + " resolves to multiple packages") ;
}
else {
List<EObject> nestedVisiblePackages ;
previousPackage = visiblePackages.get(0) ;
remaining = remaining.getRemaining() ;
while (remaining != cddClassName) {
nestedVisiblePackages = AlfScopeProvider.scopingTool.getVisiblePackages(previousPackage).resolveByName(remaining.getId()) ;
if (nestedVisiblePackages.isEmpty()) {
throw new Exception("Could not resolve package " + remaining.getId()) ;
}
else if (nestedVisiblePackages.size() > 1) {
throw new Exception(remaining.getId() + " resolves to multiple packages") ;
}
previousPackage = nestedVisiblePackages.get(0) ;
remaining = remaining.getRemaining() ;
}
}
}
// At this point, the (potential) path has been validated
// cddClassName should resolve to a classifier
List<EObject> visibleClassifiers = null ;
EObject resolvedClassifier = null ;
if (previousPackage != null)
visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(previousPackage).resolveByName(cddClassName.getId()) ;
else
visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(exp).resolveByName(cddClassName.getId()) ;
if (visibleClassifiers.isEmpty()) {
throw new Exception("Could not resolve classifier " + cddClassName.getId()) ;
}
else if (visibleClassifiers.size() > 1) {
throw new Exception(remaining.getId() + " resolves to multiple classifiers.") ;
}
else {
resolvedClassifier = visibleClassifiers.get(0) ;
}
List<EObject> visibleConstructor = AlfScopeProvider.scopingTool.getVisibleOperationsOrBehaviors(resolvedClassifier).resolveByName(cddConstructorName.getId()) ;
if (visibleConstructor.size() > 1) {
// try to match with arguments
// if does not match, raise an exception
List<SignatureFacade> visibleConstructorSignatures = new ArrayList<SignatureFacade>() ;
for (EObject cddConstructor : visibleConstructor) {
SignatureFacade cddConstructorSignature = SignatureFacadeFactory.eInstance.createSignatureFacade(cddConstructor) ;
if (cddConstructorSignature.isAConstructor())
visibleConstructorSignatures.add(cddConstructorSignature) ;
}
List<SignatureFacade> matchingSignatures = SignatureFacade.findNearestSignature(arguments, visibleConstructorSignatures) ;
if (matchingSignatures.size() > 1) {
String errorMessage = cddConstructorName.getId() + "(" ;
boolean first = true ;
for (TypeExpression arg : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += arg.getLabel() ;
}
errorMessage += ") resolves to multiple constructors" ;
throw new Exception(errorMessage) ;
}
else if (matchingSignatures.size() == 0) {
String errorMessage = "Constructor " + cddConstructorName.getId() + "(" ;
boolean first = true ;
for (TypeExpression arg : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += arg.getLabel() ;
}
errorMessage += ") is undefined" ;
throw new Exception(errorMessage) ;
}
else { // exactly one match
return matchingSignatures.get(0) ;
}
}
else if (visibleConstructor.size() == 0) {
String errorMessage = "Constructor " + cddConstructorName.getId() + "(" ;
boolean first = true ;
for (TypeExpression arg : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += arg.getLabel() ;
}
errorMessage += ") is undefined" ;
throw new Exception(errorMessage) ;
}
else { // exactly one constructor
// Tries to determine if arguments match
SignatureFacade constructor = createSignatureFacade(visibleConstructor.get(0)) ;
if (!constructor.isAConstructor()) {
// Throw an exception
String errorMessage = "Constructor " + cddConstructorName.getId() + "(";
boolean first = true ;
for (TypeExpression t : arguments) {
if (first) first = false ; else errorMessage += ", " ;
errorMessage += t.getLabel() ;
}
errorMessage += ") is undefined" ;
throw new Exception(errorMessage) ;
}
String potentialErrorMessage = constructor.isCompatibleWithMe(arguments, true) ;
if (potentialErrorMessage.length() == 0)
return constructor ;
else
throw new Exception(potentialErrorMessage) ;
}
}
throw new Exception("Not supported case") ;
}
}