/***************************************************************************** * 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.Iterator; import java.util.List; import java.util.Map; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.papyrus.uml.alf.alf.AcceptClause; import org.eclipse.papyrus.uml.alf.alf.AcceptStatement; import org.eclipse.papyrus.uml.alf.alf.AlfPackage; import org.eclipse.papyrus.uml.alf.alf.ClassificationExpression; import org.eclipse.papyrus.uml.alf.alf.Expression; import org.eclipse.papyrus.uml.alf.alf.InvocationOrAssignementOrDeclarationStatement; import org.eclipse.papyrus.uml.alf.alf.LocalNameDeclarationStatement; import org.eclipse.papyrus.uml.alf.alf.LoopVariableDefinition; import org.eclipse.papyrus.uml.alf.alf.NameExpression; import org.eclipse.papyrus.uml.alf.alf.NamedTemplateBinding; import org.eclipse.papyrus.uml.alf.alf.QualifiedNameWithBinding; import org.eclipse.papyrus.uml.alf.alf.SequenceExpansionExpression; import org.eclipse.papyrus.uml.alf.alf.SuperInvocationExpression; import org.eclipse.papyrus.uml.alf.alf.UnqualifiedName; import org.eclipse.papyrus.uml.alf.scoping.AlfScopeProvider; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.ElementImport; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.Parameter; import org.eclipse.uml2.uml.ParameterableElement; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.TemplateParameter; import org.eclipse.uml2.uml.TypedElement; public class TypeFacadeFactory { public static TypeFacadeFactory eInstance = new TypeFacadeFactory() ; public TypeFacade createTypeFacade(EObject typeObject) { TypeFacade result = new TypeFacade() { @Override public String getLabelWithoutBinding() { try { if (typeObject instanceof Classifier) return ((Classifier)typeObject).getName() ; if (typeObject instanceof ElementImport) { ElementImport eImport = (ElementImport)typeObject ; if (eImport.getAlias()!=null ) return eImport.getAlias() ; else return ((Classifier)eImport.getImportedElement()).getName() ; } if (typeObject instanceof Parameter) { return ((Parameter)typeObject).getType().getName() ; } if (typeObject instanceof TypedElement) { return ((TypedElement)typeObject).getType().getName() ; } if (typeObject instanceof SequenceExpansionExpression) { TypeFacade t = TypeFacadeFactory.eInstance.createTypeFacade(typeObject) ; return t.extractActualType().getName() ; } } catch (NullPointerException e) { // occurs when no type can be derived from typeObject (i.e., typeObject.getType() == null) return "any" ; } return super.getLabel() ; } @Override public String getLabel() { try { if (typeObject instanceof Classifier) return ((Classifier)typeObject).getName() + super.getLabel() ; if (typeObject instanceof ElementImport) { ElementImport eImport = (ElementImport)typeObject ; if (eImport.getAlias()!=null ) return eImport.getAlias() + super.getLabel() ; else return ((Classifier)eImport.getImportedElement()).getName() + super.getLabel() ; } if (typeObject instanceof Parameter) { return ((Parameter)typeObject).getType().getName() + super.getLabel() ; } if (typeObject instanceof TypedElement) { return ((TypedElement)typeObject).getType().getName() + super.getLabel() ; } if (typeObject instanceof SequenceExpansionExpression) { TypeFacade t = TypeFacadeFactory.eInstance.createTypeFacade(typeObject) ; return t.extractActualType().getName() + super.getLabel() ; } if (typeObject == null) return "any" ; } catch (NullPointerException e) { // occurs when no type can be derived from typeObject (i.e., typeObject.getType() == null) return "any" ; } return super.getLabel() ; } }; if (typeObject instanceof Classifier) result.setTypeObject(typeObject) ; else if (typeObject instanceof ElementImport) result.setTypeObject(typeObject) ; else if (typeObject instanceof Parameter) result.setTypeObject(typeObject) ; else if (typeObject instanceof LocalNameDeclarationStatement) { LocalNameDeclarationStatement statement = (LocalNameDeclarationStatement)typeObject ; if (statement.getType() != null) { result.setTypeObject(createVoidFacade(statement.getType()).typeObject); } } else if (typeObject instanceof LoopVariableDefinition) { LoopVariableDefinition loopVariable = (LoopVariableDefinition)typeObject ; if (loopVariable.getType() != null) { result.setTypeObject(createVoidFacade(loopVariable.getType()).typeObject) ; } else if (loopVariable.getExpression1() != null) { TypeExpression typeOfExpression1 = new TypeUtils().getTypeOfExpression(loopVariable.getExpression1()) ; if (loopVariable.getExpression2() != null) { TypeExpression typeOfExpression2 = new TypeUtils().getTypeOfExpression(loopVariable.getExpression2()) ; int _1_2_compatibility = typeOfExpression1.isCompatibleWithMe(typeOfExpression2) ; int _2_1_compatibility = typeOfExpression2.isCompatibleWithMe(typeOfExpression1) ; if (_1_2_compatibility == _2_1_compatibility) { if (_1_2_compatibility != 0) { result.setTypeObject(typeOfExpression1.getTypeFacade().typeObject) ; } } else { if (_1_2_compatibility > _2_1_compatibility) result.setTypeObject(typeOfExpression1.getTypeFacade().typeObject) ; else result.setTypeObject(typeOfExpression2.getTypeFacade().typeObject) ; } } else { result.setTypeObject(typeOfExpression1.getTypeFacade().typeObject) ; } } } else if (typeObject instanceof Property) { result.setTypeObject(typeObject) ; } else if (typeObject instanceof SequenceExpansionExpression) { result.setTypeObject(typeObject) ; } else if (typeObject instanceof AcceptStatement) { // first extract the accept clause AcceptClause acceptClause = (AcceptClause)((AcceptStatement)typeObject).getClause() ; if (acceptClause.getQualifiedNameList() != null && !(acceptClause.getQualifiedNameList().getQualifiedName().isEmpty())) { // TODO : getQualifiedName is a collection. Should compute the least common ancestor. TypeFacade f = TypeFacadeFactory.eInstance.createVoidFacade(acceptClause.getQualifiedNameList().getQualifiedName().get(0)) ; result.setTypeObject(f.extractActualType()) ; } else { result.setTypeObject(typeObject) ; } } return result ; } public ErrorTypeFacade createErrorTypeFacade(String message, EObject source, EStructuralFeature structuralFeature) { ErrorTypeFacade result = new ErrorTypeFacade() ; result.setMessage(message) ; result.setErrorSource(source) ; result.setStructuralFeature(structuralFeature) ; return result ; } public TypeFacade createVoidFacade(Expression exp) { NameExpression actualNameExpression = null ; for (Iterator<EObject> i = exp.eAllContents() ; i.hasNext() && actualNameExpression == null ; ) { EObject o = i.next() ; if (o instanceof NameExpression) actualNameExpression = (NameExpression)o ; } if (actualNameExpression == null) return createErrorTypeFacade("A type expression is expected", exp, AlfPackage.eINSTANCE.getConditionalTestExpression_Exp()) ; else return createVoidFacade(actualNameExpression) ; } public TypeFacade createVoidFacade(NameExpression exp) { //if (! (exp.eContainer() instanceof ClassificationExpression || // exp.eContainer() instanceof SuperInvocationExpression || // exp.eContainer() instanceof InvocationOrAssignementOrDeclarationStatement)) { // return createErrorTypeFacade("A type expression is expected", exp, AlfPackage.eINSTANCE.getNameExpression_Id()) ; //} EObject previousPackage = null ; if (exp.getPath() != null) { List<UnqualifiedName> path = exp.getPath().getNamespace() ; // first resolves the first element of the path List<EObject> visiblePackages = AlfScopeProvider.scopingTool.getVisiblePackages(exp).resolveByName(path.get(0).getName()) ; if (visiblePackages.isEmpty()) { return createErrorTypeFacade("Could not resolve package " + path.get(0).getName(), path.get(0), AlfPackage.eINSTANCE.getUnqualifiedName_Name()) ; } else if (visiblePackages.size() > 1) { return createErrorTypeFacade(path.get(0).getName() + " resolves to multiple packages", exp.getPath(), AlfPackage.eINSTANCE.getQualifiedNamePath_Namespace()) ; } else { List<EObject> nestedVisiblePackages ; previousPackage = visiblePackages.get(0) ; for (int i = 1 ; i<path.size() ; i++) { nestedVisiblePackages = AlfScopeProvider.scopingTool.getVisiblePackages(previousPackage).resolveByName(path.get(i).getName()) ; if (nestedVisiblePackages.isEmpty()) { return createErrorTypeFacade("Could not resolve package " + path.get(i).getName(), path.get(i), AlfPackage.eINSTANCE.getUnqualifiedName_Name()) ; } else if (nestedVisiblePackages.size() > 1) { return createErrorTypeFacade(path.get(i).getName() + " resolves to multiple packages", exp.getPath(), AlfPackage.eINSTANCE.getQualifiedNamePath_Namespace()) ; } previousPackage = nestedVisiblePackages.get(0) ; } } } // At this point, the path has been validated, can check the final id. EObject container = exp.eContainer() ; if (container instanceof InvocationOrAssignementOrDeclarationStatement) { InvocationOrAssignementOrDeclarationStatement cddDclStatement = (InvocationOrAssignementOrDeclarationStatement)container ; if (cddDclStatement.getVariableDeclarationCompletion() != null) { if (cddDclStatement.getTypePart_OR_assignedPart_OR_invocationPart().getSuffix() != null) { return createErrorTypeFacade("A type expression is expected", cddDclStatement, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; } if (exp.getInvocationCompletion() != null || exp.getPostfixOp() != null || exp.getPrefixOp() != null || exp.getSequenceConstructionCompletion() != null ) {// TODO: handle sequence constructions return createErrorTypeFacade("A type expression is expected", cddDclStatement, AlfPackage.eINSTANCE.getInvocationOrAssignementOrDeclarationStatement_TypePart_OR_assignedPart_OR_invocationPart()) ; } List<EObject> visibleClassifiers = null ; if (previousPackage == null) visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(exp).resolveByName(exp.getId()) ; else visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(previousPackage).resolveByName(exp.getId()) ; if (visibleClassifiers.isEmpty()) { return createErrorTypeFacade("Could not resolve classifier " + exp.getId(), exp, AlfPackage.eINSTANCE.getNameExpression_Id()) ; } else if (visibleClassifiers.size() > 1) { return createErrorTypeFacade(exp.getId() + " resolves to multiple classifiers", cddDclStatement, AlfPackage.eINSTANCE.getNameExpression_Id()) ; } return new VoidFacade(createTypeFacade(visibleClassifiers.get(0))) ; } } else if (container instanceof ClassificationExpression) { // TODO } else if (container instanceof SuperInvocationExpression) { // TODO: Not to be handled here => Should not resolve to a type } List<EObject> visibleClassifiers = null ; if (previousPackage == null) visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(exp).resolveByName(exp.getId()) ; else visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(previousPackage).resolveByName(exp.getId()) ; if (visibleClassifiers.isEmpty()) { return createErrorTypeFacade("Could not resolve classifier " + exp.getId(), exp, AlfPackage.eINSTANCE.getNameExpression_Id()) ; } else if (visibleClassifiers.size() > 1) { return createErrorTypeFacade(exp.getId() + " resolves to multiple classifiers", exp, AlfPackage.eINSTANCE.getNameExpression_Id()) ; } return new VoidFacade(createTypeFacade(visibleClassifiers.get(0))) ; } public TypeFacade createVoidFacade(QualifiedNameWithBinding exp) { QualifiedNameWithBinding remaining = exp ; EObject previousPackage = null ; if (exp.getRemaining()!=null) { // A path is specified List<EObject> visiblePackages = AlfScopeProvider.scopingTool.getVisiblePackages(exp).resolveByName(exp.getId()) ; if (visiblePackages.isEmpty()) { return createErrorTypeFacade("Could not resolve package " + exp.getId(), exp, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Id()) ; } else if (visiblePackages.size() > 1) { return createErrorTypeFacade(exp.getId() + " resolves to multiple packages", exp, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Id()) ; } else { List<EObject> nestedVisiblePackages ; previousPackage = visiblePackages.get(0) ; remaining = exp.getRemaining() ; while (remaining.getRemaining() != null) { nestedVisiblePackages = AlfScopeProvider.scopingTool.getVisiblePackages(previousPackage).resolveByName(remaining.getId()) ; if (nestedVisiblePackages.isEmpty()) { return createErrorTypeFacade("Could not resolve package " + remaining.getId(), remaining, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Id()) ; } else if (nestedVisiblePackages.size() > 1) { return createErrorTypeFacade(remaining.getId() + " resolves to multiple packages", remaining, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Id()) ; } previousPackage = nestedVisiblePackages.get(0) ; remaining = remaining.getRemaining() ; } } } // At this point, the (potential) path has been validated, can check the final id. // The last remaining.id should resolve to a classifier List<EObject> visibleClassifiers = null ; if (previousPackage != null) visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(previousPackage).resolveByName(remaining.getId()) ; else visibleClassifiers = AlfScopeProvider.scopingTool.getVisibleClassifiers(exp).resolveByName(remaining.getId()) ; if (visibleClassifiers.isEmpty()) { return createErrorTypeFacade("Could not resolve classifier " + remaining.getId(),remaining, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Id()) ; } else if (visibleClassifiers.size() > 1) { return createErrorTypeFacade(remaining.getId() + " resolves to multiple classifiers.", remaining, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Id()) ; } // Need to check that potential binding is valid Classifier resolvedClassifier = (Classifier)visibleClassifiers.get(0) ; if (!resolvedClassifier.isTemplate()) { if (remaining.getBinding()!= null) { return createErrorTypeFacade(remaining.getId() + " is not a template", remaining, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Binding()) ; } else { return new VoidFacade(createTypeFacade(visibleClassifiers.get(0))) ; } } else { if (remaining.getBinding()!= null) { // Needs to check that the binding is correct: List<ParameterableElement> orderedListOfParameteredElements = new ArrayList<ParameterableElement>() ; Map<String, ParameterableElement> mapOfParameteredElements = new HashMap<String, ParameterableElement>() ; for (TemplateParameter tp : resolvedClassifier.getOwnedTemplateSignature().getOwnedParameters()) { ParameterableElement p = tp.getParameteredElement() ; if (p != null) { orderedListOfParameteredElements.add(p) ; mapOfParameteredElements.put(((NamedElement)p).getName(), p) ; } } // Builds the substitutions map Map<TemplateParameter, ParameterableElement> substitutionsMap = new HashMap<TemplateParameter, ParameterableElement>() ; for (NamedTemplateBinding ntp : remaining.getBinding().getBindings()) { ParameterableElement formal = mapOfParameteredElements.get(ntp.getFormal()) ; if (formal == null) { return createErrorTypeFacade("Template parameter " + ntp.getFormal() + " is undefined for classifier " + remaining.getId() , ntp, AlfPackage.eINSTANCE.getNamedTemplateBinding_Formal()) ; } TypeFacade actual = createVoidFacade(ntp.getActual()) ; if (actual instanceof ErrorTypeFacade) return actual ; substitutionsMap.put(formal.getTemplateParameter(), actual.extractActualType()) ; } // Checks the number of specified substitution if (remaining.getBinding().getBindings().size() != orderedListOfParameteredElements.size()) { String errorMessage = "" ; if (remaining.getBinding().getBindings().size() > orderedListOfParameteredElements.size()) errorMessage = "Too many template bindings specified for " + remaining.getId() ; else { errorMessage = "Template bindings are missing for " + remaining.getId() ; } return createErrorTypeFacade(errorMessage, remaining, AlfPackage.eINSTANCE.getQualifiedNameWithBinding_Binding()) ; } // Now, can create the void facade, and bind it with appropriate substitutions VoidFacade boundResolvedClassifier = new VoidFacade(createTypeFacade(visibleClassifiers.get(0))) ; // HashMap<Object, EObject> actualSubsitutionsMap = new HashMap<Object, EObject>() ; // for (ParameterableElement p : orderedListOfParameteredElements) { // actualSubsitutionsMap.put(TemplateBindingUtils.getParameteredElementName(p), // subsitutionsMap.get(p) ) ; // } //boundResolvedClassifier.bindTemplate(substitutionsMap) ; return boundResolvedClassifier; } else { // Binding is not specified. It is up to the caller to perform implicit bindings. return new VoidFacade(createTypeFacade(visibleClassifiers.get(0))) ; } } } }