/******************************************************************************* * Copyright (c) 2009, 2010 SAP AG 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: * SAP AG - initial API and implementation ******************************************************************************/ package org.eclipse.ocl.examples.impactanalyzer.testutils; import java.lang.reflect.Field; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; import org.eclipse.ocl.OCLInput; import org.eclipse.ocl.ParserException; import org.eclipse.ocl.ecore.Constraint; import org.eclipse.ocl.ecore.ExpressionInOCL; import org.eclipse.ocl.ecore.OCL; import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.ocl.EnvironmentFactory; import org.eclipse.ocl.examples.testutils.BaseDepartmentTest; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import company.CompanyFactory; import company.CompanyPackage; import company.Employee; import company.Freelance; import company.impl.CompanyImpl; import company.impl.DepartmentImpl; import company.impl.DivisionImpl; import company.impl.EmployeeImpl; import company.impl.FreelanceImpl; /** * This is the super class for all tests based on the Department model. */ @Ignore public class BaseDepartmentTestWithOCL extends BaseDepartmentTest { protected ResourceSet rs = null; /** * */ protected EPackage companyPackage = null; /** * the package containing the Company/Department meta model */ protected CompanyPackage comp = null; /** * the set for all instances of {@link DepartmentImpl} */ protected Set<DepartmentImpl> allDepartments = new HashSet<DepartmentImpl>(); /** * the set of all instances of {@link FreelanceImpl} */ protected Set<FreelanceImpl> allFreelances = new HashSet<FreelanceImpl>(); /** * the set of all instances of {@link EmployeeImpl} */ protected Set<EmployeeImpl> allEmployees = new HashSet<EmployeeImpl>(); /** * a ID used to create unique names for employees and freelances */ protected int curImployeeID = 0; /** * a ID used to create unique names for departments */ protected int curDepartmentID = 0; /** * a instances of {@link EmployeeImpl} */ protected EmployeeImpl aEmployee = null; /** * a instance of {@link DepartmentImpl} */ protected DepartmentImpl aDepartment = null; /** * an instance of {@link DivisionImpl} */ protected DivisionImpl aDivision = null; /** * a instance of {@link FreelanceImpl} */ protected FreelanceImpl aFreelance = null; /** * a instance of {@link CompanyImpl} */ protected CompanyImpl aCompany = null; // constaints /** * A boss is not allowed to be a freelance */ public final String notBossFreelance = "context Department \n" + " inv NotBossFreelance: \n" + " not self.boss.oclIsTypeOf(Freelance)"; /** * there must be at least one employee older than 45 in each department */ public final String oldEmployee = "context Department \n" + " inv OldEmployee: \n" + " self.employee->exists(e | e.age > 45)"; /** * different employees must have different names */ public final String uniqueNames = "context Employee \n" + "inv UniqueNames: \n" + " Employee.allInstances()->forAll(e | \n" + " e <> self implies e.name <> self.name)"; /** * the assignment of a freelance must be between 5 and 30 */ public final String validAssignment = "context Freelance \n" + " inv ValidAssignment: \n" + " self.assignment >= 5 and self.assignment <= 30"; /** * there are at most maxJuniors allowed per department */ public final String maxJuniors = "context Department inv MaxJuniors: \n" + "self.employee->select(e|e.age < 25)->size() < self.maxJuniors\n"; /** * the boss must be older than his employees */ public final String bossIsOldest = "context Employee inv BossIsOldest: \n" + "self.age <= self.employer.boss.age\n"; /** * the boss always gets the most money */ public final String bossHighestSalary = "context Department inv BossHighestSalary: \n" + "self.employee->select(\n" + " e|e.salary >= self.boss.salary)->size() <= 1\n"; /** * this is a nasty constraint with 2 navigation path introduced by collect() stating that the salaries of the employees and * the bosses must not exceed the division's budget. */ public final String nastyConstraint = "context Division \n" + "inv nasty: \n" + "self.department->collect(d| \n" + "d.employee->including(d.boss)).salary->sum() < budget"; /** * A division is allowed a maximum number of employees of the month equal to its number of departments. If a department has no * employee of the month, another one can have two. */ public final String limitEmployeesOfTheMonth = "context Division \n" + "inv limitEmployeesOfTheMonth: \n" + "self.employeesOfTheMonth->size() <= self.department->size()"; /** * This constraint is semantically identical to limitEmployeesOfTheMonth but uses a nested derivation ( * numberEmployeesOfTheMonth is a derived attribute that uses a derived reference in its derivation expression ). */ public final String nestedDerivation = "context Division \n" + "inv nestedDerivation: \n" + "self.numberEmployeesOfTheMonth <= self.department->size()"; /** * A department must have less than 5 freelancers and less than 5 students. * Department.biggestNumberOfStudentsOrFreelancers holds the size of the bigger group (it contains an if-expression, thats why we call it nonlinear). */ public final String nonLinearDerivation = "context Department \n" + "inv nonLinearDerivation: \n" + "self.biggestNumberOfStudentsOrFreelancers < 5"; /** * Each department of a company must have less than 5 freelancers and less than 5 students. * This constraint is semantically identical to nonLinearDerivation, but the context of the * constraint and the context of the derivation expression differ this time. */ public final String longNavigationWithDerivation = "context Company \n" + "inv longNavigationWithDerivation: \n" + "self.division.department->sortedBy(biggestNumberOfStudentsOrFreelancers)->last().biggestNumberOfStudentsOrFreelancers < 5"; /** * The delta of the number of employees of the month of all divisions of a company must not be greater than 5 (semantically * questionable, but uses the same derived property twice, which is what we need to test if the collecting of them works). */ public final String eotmDeltaMax = "context Company \n" + "inv eotmDeltaMax: \n" + "self.eotmDelta <= 5"; /** * Only directors are allowed to have a secretary */ public final String divisionBossSecretary = "context Employee \n" + "inv divBossSecretary: \n" + "if self.directed->isEmpty() then \n" + " self.secretary.oclIsUndefined() \n" + "else \n" + " not self.secretary.oclIsUndefined() \n" + "endif"; /** * for security reasons, secretaries must be older than their bosses ;-) the secretary is modeled as an attribute */ public final String secretaryOlderThanBoss = "context Employee \n" + "inv secretaryOlderThanBoss: \n" + "if self.directed->notEmpty() and \n" + " not self.secretary.oclIsUndefined() then \n" + " self.age < self.secretary.age \n" + "else true \n" + "endif"; /** * A boss must be at least 10 years older than the youngest employee in the managed department. */ public final String boss10YearsOlderThanJunior = "context Department \n" + "inv boss10YearsOlderThanJunior: \n" + "let t:Tuple(boss:Employee,junior:Employee)=" + "Tuple{boss=self.boss, junior=self.employee->sortedBy(age)->first()} in \n" + "t.boss.age > t.junior.age + 10"; /** * the expenses per department must not exceed its budget */ public final String expensesRestriction = "context Department inv BudgetRestriction: \n" + "self.calcExpenses() <= self.budget"; /** * A simple allInstances() expression to test instance creation/deletion filters */ public final String simpleAllInstances = "context Employee inv InstanceCount: \n" + "Employee.allInstances()->size() = 17"; // /** // * defines how to calculate expenses: The sum of the employee's salary plus // * the boss' salary // */ // public final String expensesCalculation = "context Department \n" + "def: calcExpenses():Integer = \n" + // "self.employee->iterate(e: sum=0 | sum + self.salary) + self.boss.salary"; /** * each department must be in a division */ public final String departmentMustHaveDivision = "context Department inv departmentMustHaveDivision: \n" + "self.department2division->notEmpty()"; public final String compareBossSalaryToJuniorSalary = "context Department \n" + "inv compareBossSalaryToJuniorSalary: \n" + "let bt:Tuple(bp:Employee,bs:Integer)=Tuple{bp=self.boss, bs=self.boss.salary} in " + "let jt:Tuple(jp:Employee, js:Integer)=Tuple{jp=self.employee->sortedBy(age)->first(), js=self.employee->sortedBy(age)->first().salary} in " + "let t:Tuple(b:Tuple(p1:Employee, s1:Integer), j:Tuple(p2:Employee, s2:Integer))=" + "Tuple{b=bt, j=jt} in \n" + "t.b.p1 <> t.j.p2 implies t.b.s1 > t.j.s2 + 100"; public final String employeeInSameDepartmentAsIntern = "context Employee \n" + "inv employeeInSameDepartmentAsIntern: \n" + "self.employer = self.intern.employer"; public final String checkForBob = "context Employee inv checkForBob: Employee.allInstances()->select(e:Employee | e.name = 'Bob')->asOrderedSet()->first().oclAsType(Employee).name = 'Bob'"; public final String sumBudgetLimit = "context company::Department inv: self.sumBudget() < 10000"; /* * OCLExpression representing the constrains above */ public ExpressionInOCL notBossFreelanceAST = null; public ExpressionInOCL oldEmployeeAST = null; public ExpressionInOCL uniqueNamesAST = null; public ExpressionInOCL validAssignmentAST = null; public ExpressionInOCL maxJuniorsAST = null; public ExpressionInOCL bossIsOldestAST = null; public ExpressionInOCL bossHighestSalaryAST = null; public ExpressionInOCL expensesRestrictionAST = null; public ExpressionInOCL nastyConstraintAST = null; public ExpressionInOCL limitEmployeesOfTheMonthAST = null; public ExpressionInOCL nestedDerivationAST = null; public ExpressionInOCL nonLinearDerivationAST = null; public ExpressionInOCL longNavigationWithDerivationAST = null; public ExpressionInOCL eotmDeltaMaxAST = null; public ExpressionInOCL divisionBossSecretaryAST = null; public ExpressionInOCL secretaryOlderThanBossAST = null; public ExpressionInOCL boss10YearsOlderThanJuniorAST = null; public ExpressionInOCL departmentMustHaveDivisionAST = null; public ExpressionInOCL compareBossSalaryToJuniorSalaryAST = null; public ExpressionInOCL employeeInSameDepartmentAsInternAST = null; public ExpressionInOCL checkForBobAST = null; public ExpressionInOCL simpleAllInstancesAST = null; public ExpressionInOCL sumBudgetLimitAST = null; /* * for easy access to the model */ protected EClass companyClass = null; protected EClass division = null; protected EAttribute divisionBudget = null; protected EClass department = null; protected EAttribute departmentName = null; protected EAttribute departmentMaxJuniors = null; protected EAttribute departmentBudget = null; protected EClass employee = null; protected EAttribute employeeName = null; protected EAttribute employeeAge = null; protected EAttribute employeeSalary = null; protected EReference employeeSecretary = null; protected EAttribute employeeIsSecretary = null; protected EClass freelance = null; protected EClass student = null; protected EAttribute freelanceAssignment = null; protected EReference departmentRef = null; protected EReference departmentEmployeeOfTheMonth = null; protected EReference divisionEmployeesOfTheMonth = null; protected EAttribute numberEmployeesOfTheMonth = null; protected EAttribute biggestNumberOfStudentsOrFreelancers = null; protected EAttribute eotmDelta = null; protected EReference bossRef = null; protected EReference managedRef = null; protected EReference employerRef = null; protected EReference employeeRef = null; protected EReference directedRef = null; protected EReference internRef = null; protected EReference divisionDirector = null; /** * Declare a public String field * * @param stringFieldName * @return */ protected ExpressionInOCL getAST(String stringFieldName) { try { Field stringField = getClass().getField(stringFieldName); Field astField = getClass().getField(stringFieldName + "AST"); ExpressionInOCL result = (ExpressionInOCL) astField.get(this); if (result == null) { result = (ExpressionInOCL) parse((String) stringField.get(this), this.comp).iterator().next().getSpecification(); astField.set(this, result); } return result; } catch (Exception e) { throw new RuntimeException(e); } } protected ExpressionInOCL getNotBossFreelanceAST() { return getAST("notBossFreelance"); } protected ExpressionInOCL getOldEmployeeAST() { return getAST("oldEmployee"); } protected ExpressionInOCL getUniqueNamesAST() { return getAST("uniqueNames"); } protected ExpressionInOCL getValidAssignmentAST() { return getAST("validAssignment"); } protected ExpressionInOCL getMaxJuniorsAST() { return getAST("maxJuniors"); } protected ExpressionInOCL getBossIsOldestAST() { return getAST("bossIsOldest"); } protected ExpressionInOCL getSumBudgetLimitAST() { return getAST("sumBudgetLimit"); } protected ExpressionInOCL getBossHighestSalaryAST() { return getAST("bossHighestSalary"); } protected ExpressionInOCL getExpensesRestrictionAST() { return getAST("expensesRestriction"); } protected ExpressionInOCL getNastyConstraintAST() { return getAST("nastyConstraint"); } protected ExpressionInOCL getLimitEmployeesOfTheMonthAST() { return getAST("limitEmployeesOfTheMonth"); } protected ExpressionInOCL getNestedDerivationAST() { return getAST("nestedDerivation"); } protected ExpressionInOCL getNonLinearDerivationAST() { return getAST("nonLinearDerivation"); } protected ExpressionInOCL getlongNavigationWithDerivationAST() { return getAST("longNavigationWithDerivation"); } protected ExpressionInOCL getEotmDeltaMaxAST() { return getAST("eotmDeltaMax"); } protected ExpressionInOCL getDivisionBossSecretaryAST() { return getAST("divisionBossSecretary"); } protected ExpressionInOCL getSecretaryOlderThanBossAST() { return getAST("secretaryOlderThanBoss"); } protected ExpressionInOCL getBoss10YearsOlderThanJuniorAST() { return getAST("boss10YearsOlderThanJunior"); } protected ExpressionInOCL getDepartmentMustHaveDivisionAST() { return getAST("departmentMutsHaveDivision"); } protected ExpressionInOCL getCompareBossSalaryToJuniorSalaryAST() { return getAST("compareBossSalaryToJuniorSalary"); } protected ExpressionInOCL getEmployeeInSameDepartmentAsInternAST() { return getAST("employeeInSameDepartmentAsIntern"); } protected ExpressionInOCL getCheckForBobAST() { return getAST("checkForBob"); } protected ExpressionInOCL getSimpleAllInstancesAST() { return getAST("simpleAllInstances"); } @Override @Before public void setUp() { beforeTestMethod(); } @Override @After public void tearDown() { this.resetInstances(); } protected void beforeTestMethod() { // build up the test model buildModel(); } /** * creates a whole bunch of instances * * @param numDepartments * the number of departments * @param numEmployees * the number of employees (not freelance) per department * @param numFreelance * the number of freelance per department */ protected void createInstances(double numDepartments, int numEmployees, int numFreelance) { int maxNumJuniors = 3; int budget = 50000; this.aDivision = (DivisionImpl) CompanyFactory.eINSTANCE.createDivision(); this.aDivision.setName("The super Division"); this.aDivision.setBudget(2000000); this.aCompany = (CompanyImpl)CompanyFactory.eINSTANCE.createCompany(); this.aCompany.setDivision(this.aDivision); if (this.comp.eResource() != null) { // none of them is contained somewhere else, therefore we need them in the resource this.comp.eResource().getContents().add(this.aDivision); this.comp.eResource().getContents().add(this.aCompany); } for (double i = 0; i < numDepartments; i++) { createDepartment(numEmployees, numFreelance, maxNumJuniors, budget); } // pick some instances to which the events will be related this.aDepartment = this.allDepartments.iterator().next(); this.aDivision.getDepartment().add(this.aDepartment); this.aEmployee = this.allEmployees.iterator().next(); this.aFreelance = this.allFreelances.iterator().next(); } /** * This method fetches some meta object form the model which are used to create ModelChangeEvents later on */ private void buildModel() { this.comp = company.CompanyPackage.eINSTANCE; this.companyClass = this.comp.getCompany(); this.division = this.comp.getDivision(); this.divisionBudget = (EAttribute) this.division.getEStructuralFeature("budget"); this.department = this.comp.getDepartment(); this.departmentName = (EAttribute) this.department.getEStructuralFeature("name"); this.departmentMaxJuniors = (EAttribute) this.department.getEStructuralFeature("maxJuniors"); this.departmentBudget = (EAttribute) this.department.getEStructuralFeature("budget"); this.bossRef = (EReference) this.department.getEStructuralFeature("boss"); this.employeeRef = (EReference) this.department.getEStructuralFeature("employee"); this.departmentRef = (EReference) this.division.getEStructuralFeature("department"); this.departmentEmployeeOfTheMonth = (EReference) this.department.getEStructuralFeature("employeeOfTheMonth"); this.divisionEmployeesOfTheMonth = (EReference) this.division.getEStructuralFeature("employeesOfTheMonth"); this.numberEmployeesOfTheMonth = (EAttribute) this.division.getEStructuralFeature("numberEmployeesOfTheMonth"); this.biggestNumberOfStudentsOrFreelancers = (EAttribute) this.department.getEStructuralFeature("biggestNumberOfStudentsOrFreelancers"); this.eotmDelta = (EAttribute) this.companyClass.getEStructuralFeature("eotmDelta"); this.employee = this.comp.getEmployee(); this.employeeName = (EAttribute) this.employee.getEStructuralFeature("name"); this.employeeAge = (EAttribute) this.employee.getEStructuralFeature("age"); this.employeeSalary = (EAttribute) this.employee.getEStructuralFeature("salary"); this.employerRef = (EReference) this.employee.getEStructuralFeature("employer"); this.employeeSecretary = (EReference) this.employee.getEStructuralFeature("secretary"); this.directedRef = (EReference) this.employee.getEStructuralFeature("directed"); this.managedRef = (EReference) this.employee.getEStructuralFeature("managed"); this.internRef = (EReference) this.employee.getEStructuralFeature("intern"); this.divisionDirector = (EReference) this.companyClass.getEStructuralFeature("divisionDirector"); this.freelance = this.comp.getFreelance(); this.student = this.comp.getStudent(); this.freelanceAssignment = (EAttribute) this.freelance.getEStructuralFeature("assignment"); this.rs = new ResourceSetImpl(); this.rs.eAdapters().add(new ECrossReferenceAdapter()); this.rs.getResources().add(this.comp.eResource()); } /** * Creates a Department instance * * @param employees * number of employees which are not freelances in the department * @param freelances * the number of freelances in the department * @param maxJuniors * the value for the maxJunior attribute * @param budget * the value for the budget attribute */ protected DepartmentImpl createDepartment(int employees, int freelances, int maxNumJuniors, int budget) { DepartmentImpl dep = (DepartmentImpl) CompanyFactory.eINSTANCE.createDepartment(); dep.setName("Dep" + this.curDepartmentID); dep.setMaxJuniors(maxNumJuniors); dep.setBudget(budget); this.curDepartmentID++; EmployeeImpl e = null; FreelanceImpl f = null; for (int i = 0; i < employees; i++) { e = createEmployee(); dep.getEmployee().add(e); e.setEmployer(dep); } for (int i = 0; i < freelances; i++) { f = createFreelance(); dep.getEmployee().add(f); f.setEmployer(dep); } this.allDepartments.add(dep); if (this.comp.eResource() != null) { this.comp.eResource().getContents().add(dep); } return dep; } /** * @return an instance of {@link Employee} */ protected EmployeeImpl createEmployee() { EmployeeImpl e = (EmployeeImpl) CompanyFactory.eINSTANCE.createEmployee(); e.setName("empl" + this.curImployeeID); e.setAge(42); e.setSalary(2345); this.curImployeeID++; this.allEmployees.add(e); if (this.comp.eResource() != null) { this.comp.eResource().getContents().add(e); } return e; } /** * @return a instances of {@link Freelance} */ protected FreelanceImpl createFreelance() { FreelanceImpl f = (FreelanceImpl) CompanyFactory.eINSTANCE.createFreelance(); f.setName("empl" + this.curImployeeID); f.setAge(42); f.setAssignment(7); f.setSalary(2345); this.curImployeeID++; this.allFreelances.add(f); if (this.comp.eResource() != null) { this.comp.eResource().getContents().add(f); } return f; } /** * @param expression * to parse * @return a list of {@link Constraint}s parsed from given expression */ public static List<Constraint> parse(String expression, EPackage basePackage) { OCLInput exp = new OCLInput(expression); String nsPrefix = basePackage.getNsPrefix(); EPackage.Registry.INSTANCE.put(nsPrefix, basePackage); OCL ocl = org.eclipse.ocl.examples.impactanalyzer.util.OCL.newInstance(); ocl = OCL.newInstance(new EnvironmentFactory().createPackageContext(ocl.getEnvironment(), basePackage)); List<Constraint> result = null; try { result = ocl.parse(exp); } catch (ParserException e) { System.err.println("Error while parsing Expression:" + exp); e.printStackTrace(); } return result; } private void resetInstances() { this.rs = null; this.companyPackage = null; this.comp = null; this.allDepartments = null; this.allFreelances = null; this.allEmployees = null; this.aEmployee = null; this.aDepartment = null; this.aDivision = null; this.aFreelance = null; this.companyClass = null; this.division = null; this.divisionBudget = null; this.department = null; this.departmentName = null; this.departmentMaxJuniors = null; this.departmentBudget = null; this.employee = null; this.employeeName = null; this.employeeAge = null; this.employeeSalary = null; this.employeeSecretary = null; this.freelance = null; this.student = null; this.freelanceAssignment = null; this.departmentRef = null; this.departmentEmployeeOfTheMonth = null; this.divisionEmployeesOfTheMonth = null; this.numberEmployeesOfTheMonth = null; this.biggestNumberOfStudentsOrFreelancers = null; this.eotmDelta = null; this.bossRef = null; this.managedRef = null; this.employerRef = null; this.employeeRef = null; this.directedRef = null; } }