/* * Copyright (c) 2005, 2010 Borland Software Corporation 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: * Radek Dvorak (Borland) - initial API and implementation */ package org.eclipse.gmf.internal.validate.ocl; import java.text.MessageFormat; import java.util.HashSet; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EDataType; import org.eclipse.emf.ecore.EEnumLiteral; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EOperation; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EParameter; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.ETypedElement; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.impl.EPackageRegistryImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.gmf.internal.validate.DebugOptions; import org.eclipse.gmf.internal.validate.DefUtils; import org.eclipse.gmf.internal.validate.EDataTypeConversion; import org.eclipse.gmf.internal.validate.GMFValidationPlugin; import org.eclipse.gmf.internal.validate.Messages; import org.eclipse.gmf.internal.validate.StatusCodes; import org.eclipse.gmf.internal.validate.Trace; import org.eclipse.gmf.internal.validate.expressions.AbstractExpression; import org.eclipse.gmf.internal.validate.expressions.IEvaluationEnvironment; import org.eclipse.gmf.internal.validate.expressions.IParseEnvironment; import org.eclipse.ocl.Environment; import org.eclipse.ocl.ParserException; import org.eclipse.ocl.Query; import org.eclipse.ocl.ecore.CallOperationAction; import org.eclipse.ocl.ecore.CollectionType; import org.eclipse.ocl.ecore.Constraint; import org.eclipse.ocl.ecore.EcoreEnvironmentFactory; import org.eclipse.ocl.ecore.SendSignalAction; import org.eclipse.ocl.ecore.TypeType; import org.eclipse.ocl.expressions.ExpressionsFactory; import org.eclipse.ocl.expressions.Variable; import org.eclipse.ocl.options.ParsingOptions; class OCLExpressionAdapter extends AbstractExpression { /** * The OCL language identifier. */ public static final String OCL = "ocl"; //$NON-NLS-1$ private Query<EClassifier, EClass, EObject> query; private Environment<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> env; public OCLExpressionAdapter(String body, EClassifier context, IParseEnvironment extEnv) { super(body, context, extEnv); try { org.eclipse.ocl.ecore.OCL ocl = null; if(extEnv != null) { EcoreEnvironmentFactory factory = EcoreEnvironmentFactory.INSTANCE; if(extEnv.getImportRegistry() != null) { factory = new EcoreEnvironmentFactory(extEnv.getImportRegistry()); } ocl = org.eclipse.ocl.ecore.OCL.newInstance(factory); this.env = ocl.getEnvironment(); for(String varName : extEnv.getVariableNames()) { EClassifier type = extEnv.getTypeOf(varName); Variable<EClassifier, EParameter> varDecl = ExpressionsFactory.eINSTANCE.createVariable(); varDecl.setName(varName); varDecl.setType(type); env.addElement(varDecl.getName(), varDecl, true); } } else { EPackage.Registry registry = new EPackageRegistryImpl(); for (Object nextRegistryObject : EPackage.Registry.INSTANCE.values()) { if (nextRegistryObject instanceof EPackage) { EPackage ePackage = (EPackage) nextRegistryObject; registry.put(ePackage.getNsURI(), ePackage); } } if (context.eResource() != null && context.eResource().getResourceSet() != null) { ResourceSet resourceSet = context.eResource().getResourceSet(); EcoreUtil.resolveAll(resourceSet); HashSet<EPackage> ePackages = new HashSet<EPackage>(); for (Resource resource : resourceSet.getResources()) { ePackages.addAll(EcoreUtil.<EPackage> getObjectsByType(resource.getContents(), EcorePackage.Literals.EPACKAGE)); } for (EPackage ePackage : ePackages) { registry.put(ePackage.getNsURI(), ePackage); } } ocl = org.eclipse.ocl.ecore.OCL.newInstance(new EcoreEnvironmentFactory(registry)); this.env = ocl.getEnvironment(); } ParsingOptions.setOption(this.env, ParsingOptions.implicitRootClass(this.env), EcorePackage.Literals.EOBJECT); org.eclipse.ocl.ecore.OCL.Helper helper = ocl.createOCLHelper(); helper.setContext(context); this.query = ocl.createQuery(helper.createQuery(body)); } catch (ParserException e) { setInvalidOclExprStatus(e); } catch (IllegalArgumentException e) { setInvalidOclExprStatus(e); } catch(RuntimeException e) { setStatus(GMFValidationPlugin.createStatus( IStatus.ERROR, StatusCodes.UNEXPECTED_PARSE_ERROR, Messages.unexpectedExprParseError, e)); GMFValidationPlugin.log(getStatus()); Trace.catching(DebugOptions.EXCEPTIONS_CATCHING, e); } } public String getLanguage() { return OCL; } public boolean isLooselyTyped() { return false; } public boolean isAssignableTo(EClassifier ecoreType) { if(env == null) { return false; } EClassifier oclType = env.getUMLReflection().getOCLType(ecoreType); if(oclType == null) { return false; } return isOclConformantTo(oclType); } public boolean isAssignableToElement(ETypedElement typedElement) { if(env == null || typedElement.getEType() == null) { return false; } EClassifier oclType = env.getUMLReflection().getOCLType(typedElement); if(oclType == null) { return false; } return isOclConformantTo(oclType); } public EClassifier getResultType() { return (query != null) ? query.getExpression().getType() : super.getResultType(); } protected Object doEvaluate(Object context) { return filterOCLInvalid((query != null) ? query.evaluate(context) : null); } protected Object doEvaluate(Object context, IEvaluationEnvironment extEnvironment) { if(query != null) { query.getEvaluationEnvironment().clear(); for (String varName : extEnvironment.getVariableNames()) { query.getEvaluationEnvironment().add(varName, extEnvironment.getValueOf(varName)); } } return doEvaluate(context); } private Object filterOCLInvalid(Object object) { return (env != null && object == env.getOCLStandardLibrary().getOclInvalid()) ? null : object; } private boolean isOclConformantTo(EClassifier anotherOclType) { EClassifier thisOclType = getResultType(); boolean isTargetCollection = anotherOclType instanceof CollectionType; if(isTargetCollection) { CollectionType oclCollectionType = (CollectionType)anotherOclType; if(oclCollectionType.getElementType() != null) { anotherOclType = oclCollectionType.getElementType(); } } if(thisOclType instanceof CollectionType) { if(!isTargetCollection) { return false; // can't assign CollectionType to scalar } CollectionType thisOclCollectionType = (CollectionType)thisOclType; if(thisOclCollectionType.getElementType() != null) { thisOclType = thisOclCollectionType.getElementType(); } } // handle OCL TypeType if(thisOclType instanceof TypeType) { EClassifier thisRefferedClassifier = ((TypeType)thisOclType).getReferredType(); if(thisRefferedClassifier != null) { return DefUtils.getCanonicalEClassifier(anotherOclType).isInstance(thisRefferedClassifier); } } // Note: in OCL, Double extends Integer if ((thisOclType.getInstanceClass() == Integer.class || thisOclType.getInstanceClass() == int.class) && (anotherOclType.getInstanceClass() == Double.class || anotherOclType.getInstanceClass() == double.class)) { return true; } if(thisOclType instanceof EDataType && anotherOclType instanceof EDataType) { if(new EDataTypeConversion().isConvertable((EDataType)anotherOclType, (EDataType)thisOclType)) { return true; } } return DefUtils.checkTypeAssignmentCompatibility(anotherOclType, thisOclType); } void setInvalidOclExprStatus(Exception exception) { String message = MessageFormat.format( Messages.invalidExpressionBody, new Object[] { getBody(), exception.getLocalizedMessage() }); setStatus(GMFValidationPlugin.createStatus( IStatus.ERROR, StatusCodes.INVALID_VALUE_EXPRESSION, message, exception)); Trace.catching(DebugOptions.EXCEPTIONS_CATCHING, exception); } }