| /******************************************************************************* |
| * |
| * Copyright (c) 2013, 2015 Intecs SpA |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v2.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v20.html |
| * |
| * Contributors: |
| * Nicholas Pacini nicholas.pacini@intecs.it |
| * Stefano Puri stefano.puri@intecs.it |
| * Laura Baracchi laura.baracchi@intecs.it |
| * Initial API and implementation and/or initial documentation |
| *******************************************************************************/ |
| |
| package org.polarsys.chess.contracts.validation.constraints; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.validation.AbstractModelConstraint; |
| import org.eclipse.emf.validation.IValidationContext; |
| import org.eclipse.emf.validation.model.ConstraintStatus; |
| import org.eclipse.papyrus.MARTE.MARTE_Annexes.VSL.DataTypes.BoundedSubtype; |
| import org.eclipse.papyrus.MARTE.MARTE_Annexes.VSL.DataTypes.CollectionType; |
| import org.eclipse.uml2.uml.Association; |
| import org.eclipse.uml2.uml.Class; |
| import org.eclipse.uml2.uml.Component; |
| import org.eclipse.uml2.uml.Connector; |
| import org.eclipse.uml2.uml.ConnectorEnd; |
| import org.eclipse.uml2.uml.Constraint; |
| import org.eclipse.uml2.uml.DataType; |
| import org.eclipse.uml2.uml.Element; |
| import org.eclipse.uml2.uml.Enumeration; |
| import org.eclipse.uml2.uml.Model; |
| import org.eclipse.uml2.uml.NamedElement; |
| import org.eclipse.uml2.uml.Package; |
| import org.eclipse.uml2.uml.Parameter; |
| import org.eclipse.uml2.uml.Port; |
| import org.eclipse.uml2.uml.Property; |
| import org.eclipse.uml2.uml.Signal; |
| import org.eclipse.uml2.uml.Stereotype; |
| import org.eclipse.uml2.uml.Type; |
| import org.eclipse.uml2.uml.TypedElement; |
| import org.polarsys.chess.contracts.integration.ToolIntegration; |
| import org.polarsys.chess.contracts.profile.chesscontract.ComponentInstance; |
| import org.polarsys.chess.contracts.profile.chesscontract.ContractProperty; |
| import org.polarsys.chess.contracts.profile.chesscontract.ContractRefinement; |
| import org.polarsys.chess.contracts.profile.chesscontract.DataTypes.ContractTypes; |
| |
| public class OCRAConstraints extends AbstractModelConstraint { |
| |
| |
| private static final String BLOCK = "SysML::Blocks::Block"; |
| private static final String FLOWPORT = "SysML::PortAndFlows::FlowPort"; |
| private static final String FLOWPORTMARTE = "MARTE::MARTE_DesignModel::GCM::FlowPort"; |
| private static final String SYSVIEW = "CHESS::Core::CHESSViews::SystemView"; |
| private static final String COMPVIEW = "CHESS::Core::CHESSViews::ComponentView"; |
| private static final String FORMPROP = "CHESSContract::FormalProperty"; |
| // private static final String DELEGATION = "CHESSContract::DelegationConstraint"; |
| private static final String SYSTEM = "CHESSContract::System"; |
| private static final String SUBSYSTEM = "CHESSContract::SubSystem"; |
| private static final String CONTRACTPROP = "CHESSContract::ContractProperty"; |
| private static final String COMPINST = "CHESSContract::ComponentInstance"; |
| public static final String BOUNDEDSUBTYPE = "MARTE::MARTE_Annexes::VSL::DataTypes::BoundedSubtype"; |
| public static final String COLLECTIONTYPE = "MARTE::MARTE_Annexes::VSL::DataTypes::CollectionType"; |
| |
| @Override |
| public IStatus validate(IValidationContext ctx) { |
| System.out.println("Checking model for NuSMV3 analysis tool"); |
| |
| IStatus success = ctx.createSuccessStatus(); |
| IStatus failure = ctx.createFailureStatus(); |
| // This invariant may raise multiple errors |
| Collection<ConstraintStatus> allResults = new ArrayList<ConstraintStatus>(); |
| |
| // Retrieve the model and the SystemView package |
| Model model = (Model) ctx.getTarget(); |
| Package sysView = null; |
| Package compView = null; |
| for (Package pkg : model.getNestedPackages()) { |
| if(pkg.getAppliedStereotype(SYSVIEW) != null){ |
| sysView = pkg; |
| } |
| if(pkg.getAppliedStereotype(COMPVIEW) != null){ |
| compView = pkg; |
| } |
| } |
| // if(sysView == null){ |
| // //return failure; |
| // } |
| //else: |
| // Browse through the model and get all blocks, ports, properties and associations |
| List<Class> allBlocks = new ArrayList<Class>(); |
| List<Port> allFlowPorts = new ArrayList<Port>(); |
| List<Property> allProperties = new ArrayList<Property>(); |
| List<Association> allAssociations = new ArrayList<Association>(); |
| List<Connector> allConnectors = new ArrayList<Connector>(); |
| List<Constraint> allFormalProps = new ArrayList<Constraint>(); |
| List<Property> allContractProperties = new ArrayList<Property>(); |
| List<Parameter> allParameters = new ArrayList<Parameter>(); |
| |
| if (sysView != null) |
| |
| for (Element elem : sysView.allOwnedElements()) { |
| if (elem.getAppliedStereotype(BLOCK) != null || elem.getAppliedStereotype(SUBSYSTEM) != null || |
| elem.getAppliedStereotype(SYSTEM) != null) { |
| allBlocks.add((Class) elem); |
| } |
| if (elem instanceof Property){ |
| if (elem.getAppliedStereotype(FLOWPORT) != null || elem.getAppliedStereotype(FLOWPORTMARTE) != null){ |
| allFlowPorts.add((Port) elem); |
| }else if(elem.getAppliedStereotype(CONTRACTPROP) != null){ |
| allContractProperties.add((Property) elem); |
| }else if(!(elem instanceof Port)){ |
| allProperties.add((Property) elem); |
| } |
| } |
| if (elem instanceof Association){ |
| allAssociations.add((Association) elem); |
| } |
| if (elem instanceof Connector){ |
| allConnectors.add((Connector) elem); |
| } |
| if (elem instanceof Constraint && elem.getAppliedStereotype(FORMPROP) != null){ |
| allFormalProps.add((Constraint)elem); |
| } |
| if (elem instanceof Parameter){ |
| if (((Parameter) elem).getOperation()!= null) |
| allParameters.add((Parameter) elem); |
| } |
| } |
| |
| if (compView != null) |
| for (Element elem : compView.allOwnedElements()) { |
| if (elem instanceof Constraint && elem.getAppliedStereotype(FORMPROP) != null){ |
| allFormalProps.add((Constraint)elem); |
| } |
| |
| if (elem instanceof Property){ |
| if (elem.getAppliedStereotype(CONTRACTPROP) != null){ |
| allContractProperties.add((Property) elem); |
| }else if(!(elem instanceof Port) && !(elem.getOwner() instanceof DataType)){ |
| allProperties.add((Property) elem); |
| } |
| } |
| if (elem instanceof Parameter){ |
| if (((Parameter) elem).getOperation()!= null) |
| allParameters.add((Parameter) elem); |
| } |
| } |
| |
| if (sysView == null){ |
| //we are not in a CHESS model, se we have to consider the entire Model for checking |
| for (Element elem : model.allOwnedElements()) { |
| if (elem.getAppliedStereotype(BLOCK) != null || elem.getAppliedStereotype(SUBSYSTEM) != null || |
| elem.getAppliedStereotype(SYSTEM) != null) { |
| allBlocks.add((Class) elem); |
| } |
| if (elem instanceof Property){ |
| if (elem.getAppliedStereotype(FLOWPORT) != null || elem.getAppliedStereotype(FLOWPORTMARTE) != null){ |
| allFlowPorts.add((Port) elem); |
| }else if(elem.getAppliedStereotype(CONTRACTPROP) != null){ |
| allContractProperties.add((Property) elem); |
| }else if(!(elem instanceof Port) && !(elem.getOwner() instanceof DataType)){ |
| allProperties.add((Property) elem); |
| } |
| } |
| if (elem instanceof Association){ |
| allAssociations.add((Association) elem); |
| } |
| if (elem instanceof Connector){ |
| allConnectors.add((Connector) elem); |
| } |
| if (elem instanceof Constraint && elem.getAppliedStereotype(FORMPROP) != null){ |
| allFormalProps.add((Constraint)elem); |
| } |
| if (elem instanceof Parameter){ |
| if (((Parameter) elem).getOperation()!= null) |
| allParameters.add((Parameter) elem); |
| } |
| |
| } |
| } |
| |
| allResults.addAll(checkConstraintForeverAnalysis(allBlocks, allFlowPorts, allProperties, allAssociations, allConnectors, allFormalProps, allContractProperties, allParameters,ctx)); |
| |
| if (allResults.size()>0) { |
| //size of all results > 0, create multistatus |
| return ConstraintStatus.createMultiStatus(ctx, allResults); |
| } else { |
| //no errors, return success |
| return success; |
| } |
| } |
| |
| private Collection<? extends ConstraintStatus> checkConstraintForeverAnalysis(List<Class> allBlocks, List<Port> allFlowPorts, List<Property> allProperties, List<Association> allAssociations, List<Connector> allConnectors, List<Constraint> allFormalProps, List<Property> allContractProperties, List<Parameter> allParameters,IValidationContext ctx) { |
| |
| Collection<ConstraintStatus> results = new ArrayList<ConstraintStatus>(); |
| String errorMsg; |
| /** |
| * Here we check that for each block, their names contain only allowed characters |
| * we also check that each block has attributes with unique names |
| */ |
| for (Class block : allBlocks) { |
| |
| if(!checkName(block)){ |
| errorMsg = "The Block named " + block.getName() +" contains unallowed character(s)"; |
| results.add(createConstraintStatus(block, errorMsg, ctx)); |
| } |
| if(hasAmbiguousAttributes(block)){ |
| errorMsg = "There are 2 or more attributes with the same name in Block: " + block.getName(); |
| results.add(createConstraintStatus(block, errorMsg, ctx)); |
| } |
| |
| } |
| |
| /** |
| * Here we check that all the FlowPorts of the model are typed |
| * and that their names contain only allowed characters |
| * and that no unsupported types are used |
| */ |
| for (Port flowport : allFlowPorts) { |
| if(flowport.getType() == null){ |
| errorMsg = "The FlowPort " + flowport.getName() +" should be typed"; |
| results.add(createConstraintStatus(flowport, errorMsg, ctx)); |
| } |
| if(!checkName(flowport)){ |
| errorMsg = "The FlowPort named " + flowport.getName() +" contains unallowed character(s)"; |
| results.add(createConstraintStatus(flowport, errorMsg, ctx)); |
| } |
| if (!hasValidType(flowport)){ |
| errorMsg = "The property named " + flowport.getName() + " refer to an unallowed type"; |
| results.add(createConstraintStatus(flowport, errorMsg, ctx)); |
| } |
| } |
| |
| /** |
| * Here we check that for each Property (part) its name contain only allowed characters |
| * Here we check that no unsupported types are used |
| */ |
| for (Property prop : allProperties) { |
| if(!checkName(prop)){ |
| errorMsg = "The property named " + prop.getName() +" in block " + ((NamedElement) prop.getOwner()).getName() + " contains unallowed character(s)"; |
| results.add(createConstraintStatus(prop, errorMsg, ctx)); |
| } |
| if (!(prop.getType().getAppliedStereotype(BLOCK) != null) && !(prop.getType() instanceof Component)){ |
| if (!hasValidType(prop)){ |
| errorMsg = "The property named " + prop.getName() +" in entity " + ((NamedElement) prop.getOwner()).getName() + " refer to an unallowed type"; |
| results.add(createConstraintStatus(prop, errorMsg, ctx)); |
| } |
| } |
| } |
| |
| /** |
| * Here we check that the contract properties are correctly refined wrt weak/strong |
| * contracts in component instances |
| **/ |
| for (Property contrProp : allContractProperties) { |
| if(!checkContractPropertyRefinement(contrProp)){ |
| errorMsg = "Contract Refinement for " + contrProp.getName() + " is not correct (check weak guarantees)"; |
| results.add(createConstraintStatus(contrProp, errorMsg, ctx)); |
| } |
| } |
| |
| /** |
| * Here we check that all the associations are directed |
| * (the association should have exactly one ownedEnd) |
| */ |
| for (Association assoc : allAssociations) { |
| if(assoc.getOwnedEnds().size() != 1){ |
| errorMsg = "The association between " + assoc.getMemberEnds().get(0).getName() +" and " + |
| assoc.getMemberEnds().get(1).getName() + " is not directed"; |
| results.add(createConstraintStatus(assoc, errorMsg, ctx)); |
| } |
| } |
| |
| /** |
| * Here we check that no unsupported types are used |
| */ |
| for (Parameter param : allParameters) { |
| if (!hasValidType(param)){ |
| errorMsg = "The Parameter named " + param.getName() +" in entity " + ((NamedElement) param.getOwner()).getName() + " refer to an unallowed type"; |
| results.add(createConstraintStatus(param, errorMsg, ctx)); |
| } |
| } |
| |
| /** |
| * Here we check that ports are correctly connected |
| * i.e the type and the direction of the ports are compatible |
| * (the direction depends on whether the connectors is a port delegation or not |
| */ |
| Port firstPort, secondPort; |
| Stereotype firstStereo, secondStereo; |
| ConnectorEnd firstEnd, secondEnd; |
| |
| for (Connector conn : allConnectors){ |
| |
| firstEnd = conn.getEnds().get(0); |
| secondEnd = conn.getEnds().get(1); |
| if(firstEnd.getRole() instanceof Port && secondEnd.getRole() instanceof Port){ |
| firstPort = (Port) firstEnd.getRole(); |
| firstStereo = firstPort.getAppliedStereotype(FLOWPORT); |
| if(firstStereo == null){ |
| firstStereo = firstPort.getAppliedStereotype(FLOWPORTMARTE); |
| } |
| secondPort = (Port) secondEnd.getRole(); |
| secondStereo = secondPort.getAppliedStereotype(FLOWPORT); |
| if(secondStereo == null){ |
| secondStereo = firstPort.getAppliedStereotype(FLOWPORTMARTE); |
| } |
| |
| //check types |
| if(firstPort.getType() != null && secondPort.getType() != null){ |
| if(!(firstPort.getType().getName().equals(secondPort.getType().getName()))){ |
| errorMsg = "This connector must connect ports of the same type"; |
| results.add(createConstraintStatus(conn, errorMsg, ctx)); |
| } |
| } |
| //check directions: |
| //delegation: in -> in; out -> out |
| //connection: in -> out; out -> in |
| if(firstStereo != null && secondStereo != null){ |
| String firstDir = firstPort.getValue(firstStereo, "direction").toString(); |
| String secondDir = secondPort.getValue(secondStereo, "direction").toString(); |
| Property prFirst = firstEnd.getPartWithPort(); |
| Property prSec = secondEnd.getPartWithPort(); |
| //check that the one of the properties contains the other (delegation) |
| if(prFirst == null ^ prSec == null){ |
| if(!firstDir.equals(secondDir)){ |
| errorMsg = "Connector " + conn.getQualifiedName() + " is delegating ports with incompatible direction"; |
| results.add(createConstraintStatus(conn, errorMsg, ctx)); |
| } |
| }else{ //is connection |
| if(firstDir.equals(secondDir)){ |
| errorMsg = "Connector " + conn.getQualifiedName() + " is connecting ports with incompatible direction"; |
| results.add(createConstraintStatus(conn, errorMsg, ctx)); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Here we check that each FormalProperty passes the syntax check provided by NuSMV3-OCRA |
| */ |
| //issues with ocra 1.3.0 under Windows |
| ToolIntegration checker = new ToolIntegration(null); |
| |
| for (Constraint formalProp : allFormalProps) { |
| if(!checker.checkFormalProperty(formalProp)){ |
| errorMsg = "The formal property " + formalProp.getName() + " is not correct. Please check its syntax"; |
| results.add(createConstraintStatus(formalProp, errorMsg, ctx)); |
| } |
| } |
| |
| return results; |
| } |
| |
| |
| private boolean checkContractPropertyRefinement(Property prop) { |
| ContractProperty contrProp = (ContractProperty) prop.getStereotypeApplication(prop.getAppliedStereotype(CONTRACTPROP)); |
| EList<ContractRefinement> contrRefList = contrProp.getRefinedBy(); |
| if(contrRefList.size() == 0){ |
| return true; |
| } |
| for (ContractRefinement contrRef : contrRefList) { |
| ContractProperty cp = contrRef.getContract(); |
| ContractTypes ctype = cp.getContractType(); |
| if (ctype == ContractTypes.WEAK) { |
| Property p = contrRef.getInstance(); |
| Stereotype stereo = p.getAppliedStereotype(COMPINST); |
| if(stereo != null){ |
| ComponentInstance compInst = (ComponentInstance) p.getStereotypeApplication(stereo); |
| if(!compInst.getWeakGuarantees().contains(cp)){ |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| //used to check whether the block has attributes with ambiguous names (i.e. not unique) |
| private boolean hasAmbiguousAttributes(Class block) { |
| |
| EList<Property> attributes = block.getAllAttributes(); |
| Set<String> names = new HashSet<String>(); |
| for (Property prop : attributes) { |
| names.add(prop.getName()); |
| } |
| if(names.size() != attributes.size()){ |
| return true; |
| } |
| return false; |
| } |
| |
| |
| //used to check whether the name of elem contains only allowed characters or not |
| private boolean checkName(NamedElement elem) { |
| |
| if(elem.getName().contains(" ")){ |
| return false; |
| } |
| |
| return true; |
| |
| // if(elem.getName().matches("[!A-Za-z0-9]*")){ //probably too strict: check NuMSV3 |
| // return true; |
| // } |
| // return false; |
| } |
| |
| |
| |
| private boolean hasValidType(TypedElement typed){ |
| Type type = typed.getType(); |
| if (type == null) |
| return false; |
| |
| String typeName = type.getName(); |
| if (typeName.compareToIgnoreCase("integer")==0||typeName.compareToIgnoreCase("boolean")==0|| |
| typeName.compareTo("real")==0||typeName.compareToIgnoreCase("continuous")==0|| |
| typeName.compareTo("event")==0) |
| return true; |
| if (type instanceof Signal) |
| return true; |
| if(type instanceof Enumeration) |
| return true; |
| Stereotype boundedste = type.getAppliedStereotype(BOUNDEDSUBTYPE); |
| //check for ranges |
| if (boundedste != null){ |
| //only range of integers are supported by Ocra |
| BoundedSubtype subtype = (BoundedSubtype) type.getStereotypeApplication(boundedste); |
| if (subtype.getBaseType().getName().compareToIgnoreCase("integer")==0){ |
| return true; |
| } |
| } |
| Stereotype collectiontypeStereo = type.getAppliedStereotype(COLLECTIONTYPE); |
| if (collectiontypeStereo != null){ |
| CollectionType collType = (CollectionType) type.getStereotypeApplication(collectiontypeStereo); |
| Property collProp = collType.getCollectionAttrib(); |
| if (collProp == null) |
| return false; |
| if (!hasValidType(collProp)) |
| return false; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| //utility method to create a constraint status in case of error |
| private ConstraintStatus createConstraintStatus(EObject object, String errorMsg, IValidationContext ctx) { |
| |
| Collection<EObject> resultLocus = new ArrayList<EObject>(); |
| resultLocus.add(object); |
| return ConstraintStatus.createStatus(ctx, object, resultLocus, IStatus.ERROR,3, errorMsg, (Object[]) null); |
| |
| } |
| |
| } |