diff --git a/README.md b/README.md index b430448fa85203493c0923367aaf81b13d8635fe..91860e940f4c4d7a9714cda190d78100e47ca92a 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,84 @@ # MDSD Example: Transportation Network - Sources -## Exercise 01 - Metamodel +## Exercise 05 - Model Validation and Testing -This repository includes the resources of the first task of the MDSD Example "Transportation Network". +This repository includes the resources of the 5. task of the MDSD Example "Transportation Network". ## Task Description -The main task of the exercise is to create a **metamodel for the domain of transportation networks**. +his exercise provides an introduction to **Model Validation** and **Testing of Model Transformations**. While similar, model validation focuses on validating syntactic or semantic constraints inside models. Model test engines validate the correctness of model transformations like model-to-model transformations or model rewrites. -To realize that, we use the *Eclipse Modeling Framework* to create the metamodel and afterwards prepare **multiple networks** to test the metamodel. These also double as test models for following exercises. +In this exercise, we will focus on validation and testing using the already known *Epsilon*[^epsilon] language family [@kolovos2010epsilon], particularly the **Epsilon Validation Language (EVL)**, to validate our *transportation network* models, and **EUnit**, which is a unit test framework for EMF, XML, JSON, ... models -To do so, complete the following subtasks: +[^epsilon]: [https://www.eclipse.org/epsilon/](https://www.eclipse.org/epsilon/) -1. Create an `Ecore Modeling Project` for your *transportation network metamodel* in your *Eclipse Modeling Tool* instance. +1. At first, you need to prepare an Epsilon EVL environment: - - `New...`➔`Other...`➔`Ecore Modeling Project` - - Name the project as you like - - The most important information is the `main package name` (second page) which will be the name of your metamodel and the file name extension of future models. + 1. Open an *Eclipse* workspace with your *transportation network* metamodel projects or import them into a new workspace. `Run` one metamodel project `as` an `Eclipse Application` to register the metamodel in *Eclipse*. **In the new *Eclipse* instance** you can start using *Epsilon*. -2. Model your metamodel in the `*.ecore` file. Use the editor that should open on project creation. See the **description of the transportation network** for details. - - - You only need to use the `Class`, `Attribut`, `Enumeration` `Reference`, `Composition`, and `Supertype` meta-elements for this example. - - Try to design the `Class`es connected by `Composition`s like a tree (containment hierarchy).The root of the tree should be an `Class` that represence your model itself ( for example `Transportation Network`). All classes except a root class should have (at least) one incoming composition. Use `Reference`s for non-containment relationships. - - Try too specify the properties of the metamodel elements as clear as possible in the property explorer below the diagram (The explorer shows the properties of the element selected in the diagram). + 2. *Epsilon* does not need specific projects to work. Create a new general `Project` in your workspace and create a new `*.evl` file. -3. Create the `Model`, `Edit`, and `Editor` implementation code through the `*.genmodel` file (open the file and right-click on the root element). This will generate the metamodel implementation and an editor plugin for *Eclipse*. -4. Right-click on the model project and choose `Run As...` and then `Eclipse Application`. A new *runtime* *Eclipse* instance will open. -5. There, create a new general `Project` with the `New...` option and a model `*.[your metamodel name]` with `New...`➔`Other...`➔`search for your metamodel name` (Please choose the metamodel's root element as `Model Object` in the following wizard). You should now be able to model your state machine with a *simple tree editor* by opening the file. + 3. You can now declare your model validation rules in this `*.evl` file (see Task 2 for details). To familiarize yourself with *EVL* and *Epsilon*, you can use the documentation[^epsilonDokuEVL][^epsilonDokuEOL], examples[^epsilonExample], or the playground[^epsilonPlayground]. -**The domain you need to metamodel is described as following:** + 4. To run the transformation, `right-click` on the `*.evl` file and choose `Run As -> Run Configuration`. In the next window, create a new `EVL Validation` configuration, select your `*.evl` file under `Source`, and add your `source` model under `Models` (as `EMF Models`, `source` is an example name, the names of the models should be the same as the ones used in your *EVL* file). After that, you can always re-run the configuration and start the validation by clicking on `Run` in the menu. -The first part of the domain describes the transportation network itself: +[^epsilonDokuEVL]: [https://www.eclipse.org/epsilon/doc/evl/](https://www.eclipse.org/epsilon/doc/evl/) +[^epsilonDokuEOL]: [https://www.eclipse.org/epsilon/doc/eol/](https://www.eclipse.org/epsilon/doc/eol/) +[^epsilonExample]: [https://www.eclipse.org/epsilon/examples/\#epsilon-transformation-language](https://www.eclipse.org/epsilon/examples/\#epsilon-validation-language) +[^epsilonPlayground]: [https://eclipse.dev/epsilon/playground/](https://eclipse.dev/epsilon/playground/) -> A `Transportation Network` must have an `identifier` to refer to it in the database of the users. It consists of multiple `Locations` and `Road Networks`. A location can have a `name` and can include multiple `Points Of Interest`. Each point of interest (POI) can include a `name`. There are different types of POIs: -> -> - A `Rest Stop` were drivers can take their mandetory breaks. -> - A `Gas Station` which could serve one or more`Fuel Types` like `Petrol`, `Diesel`, and `Electricity`. -> - `Warehouses` that could be `Company Warehouses` (owned by the user) or `Customer Warehouses` (Here the `customer name` should be included) -> - Last, there can be `Other` POIs which have a general `type`. -> -> Road networks must have an `identifier` and a `target` and `source` location. They can be marked as `directed` to allow mono- or bidirectional usage. They also can include the `length` of the network for later fuel calculations. Each network includes multiple, but at least one, `Road`s. Each road must be `identifiable` and also include their `length`, so that later the network length could be calculated. +1. Model validation checks if syntactic and semantic constraints defined in validation rules are met within your model. They often apply automatic rewriting if possible or propose fixes for the user. -The second part of the metamodel includes information for the usage of the transportation network: + 1. Write warnings for minor problems in your transportation network models. Examples of such problems are: + - Elements without names or identifiers + - Road Networks length is still `-1`. + - A Location is isolated in the model. + - Numbers are outside of a specific/normal range. + - Road Networks have no Roads + - There is no company warehouse in the model -> The transportation network also includes `Route`s that can be planed for transportation requests. Each route has an `identifier`and can have a `start` location, `end` location and/or a `list of road networks` the route includes. To execute the routes the transportation network also includes `Vehicle`s. These can be free to use (are in the `car park` of the company) or already included in a route. One route is always driven by one vehicle. The vehicle itself must have an `identifier`, `model`, `fuel capacity` and a `fuel type`. They can include a `location` to help their management for routes in case they are free to use. + 2. Write errors for problems that need to be addressed in your transportation network models. Examples of such problems are: + - Road Networks and Routes are missing a target or source. + - Routes have no references to road networks. + - The source of a route has no `Company Warehouse` POI + - The target of a route has no `Customer Warehouse` POI + - A Route has no `Vehicle` registered + - A Route is longer than a vehicle´s fuel capacity and has no `Gas Station` POI + + 3. Try to write automatic rewrites for the errors if possible or try to provide fixes to propose to the user. + +3. Next, you need to prepare an Epsilon EUnit environment: + 1. Download the **Epsilon Gradle Example**[^epsilongradleexample] and use it as a base. + + 2. If needed, create a new `*.eunit` file in the `./scripts` folder to write your model tests or use the existing file. + + 3. You can now declare your model testing rules in the `*.eunit` files (see Task 4 for details). To familiarize yourself with *EUnit* and *Epsilon*, you can use the documentation[^epsilonDokuEUnit][^epsilonDokuEOL], examples[^epsilonExampleEUnit], or the playground[^epsilonPlayground]. + + 4. Also, you can take a look at the documentation of the epsilon workflow support [^epsilongradle][^epsilonant] + + 5. To run the transformation, `right-click` on the `*.eunit` file and choose `Run As -> Run Configuration`. In the next window, create a new `EVL Validation` configuration, select your `*.eunit` file under `Source`, and add both your `source` and `target` models under `Models` (as `EMF Models`, `source` is an example name, the names of the models should be the same as the ones used in your *EUnit* file). After that, you can always re-run the configuration and start the transformation by clicking on `Run` in the menu. + +[^epsilonDokuEUnit]: [https://www.eclipse.org/epsilon/doc/eunit/](https://www.eclipse.org/epsilon/doc/eunit/) +[^epsilongradleexample]: [https://git-st.inf.tu-dresden.de/open-teaching-resources/model-driven-software-development/transportation_network-mdsd-pipeline/transportation_network-mdsd-epsilongradleexample](https://git-st.inf.tu-dresden.de/open-teaching-resources/model-driven-software-development/transportation_network-mdsd-pipeline/transportation_network-mdsd-epsilongradleexample) +[^epsilongradle]: [https://eclipse.dev/epsilon/doc/articles/running-epsilon-ant-tasks-from-command-line/#gradle](https://eclipse.dev/epsilon/doc/articles/running-epsilon-ant-tasks-from-command-line/#gradle) +[^epsilonant]: [https://eclipse.dev/epsilon/doc/workflow/](https://eclipse.dev/epsilon/doc/workflow/) +[^epsilonExampleEUnit]: [https://www.eclipse.org/epsilon/examples/\#eunit](https://www.eclipse.org/epsilon/examples/\#eunit) + +4. Model testing checks for semantic or syntactic errors introduced by other model transformations or rewrites. It can be compared to unit testing your MDSD *'programs'*. + + 1. Prepare a target model of a transformation you want to check and an expected model. + 1. Create a `.eunit` file to write your tests. + 2. Compare the target model with the expected model via a `EUnit` `@Test` + + 2. Prepare a source and a target model of a transformation you want to check. + 1. Try to abstract failure points from your model transformation and create a @Test for each test case. + 2. Abstract the expected rewrites from the source model and write assertions, checking if these rewrites exist in the target model. + + 3. (Optional) Prepare a source and a target model of a transformation you want to check. + 1. Try to use an EVL script inside your tests to check for validation problems caused by the transformation in the target model. ## How To Run the Base Solution -1. `Import` the `stgroup.mdsd.transportation_network.*` projects in your *Eclipse* workspace. -2. In the runtime *Eclipse* instance `import` the `model_examples` project to inspect and modify the example *transportation network* models. +1. `Import` the `stgroup.mdsd.transportation_network.metamodel.*` projects in your *Eclipse* workspace. +2. In the runtime *Eclipse* instance `import` the `model_examples` and the `stgroup.mdsd.transportation_network.metamodel` project and run the epsilone engine like it us described in exercise 4. +3. Run the gradle project in `stgroup.mdsd.transportation_network.test` like it is described in its `README.md` diff --git a/model_examples/saxony.transportation_network b/model_examples/saxony.transportation_network index bfce3809907559867d10dd80ce27a8e71323ff2e..4c5cec497f8cedcbe2383c2fbff0b7132e19a561 100644 --- a/model_examples/saxony.transportation_network +++ b/model_examples/saxony.transportation_network @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <transportation_network:TransportationNetwork xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:transportation_network="http://www.stgroup.org/transportation_network" identifier="saxony"> - <roadnetworks identifier="DG" length="95.0" source="//@locations.0" target="//@locations.3"> + <roadnetworks identifier="DG" length="95.0" target="//@locations.3"> <roads identifier="A4" length="95.0"/> </roadnetworks> <roadnetworks identifier="DL" length="102.0" source="//@locations.0" target="//@locations.2"> @@ -8,12 +8,10 @@ <roads identifier="A14" length="67.0"/> <roads identifier="A38" length="15.0"/> </roadnetworks> - <roadnetworks identifier="DC" length="64.0" source="//@locations.0" target="//@locations.1"> + <roadnetworks identifier="DC" source="//@locations.0" target="//@locations.1"> <roads identifier="A4" length="64.0"/> </roadnetworks> - <roadnetworks identifier="LC" length="69.0" source="//@locations.2" target="//@locations.1"> - <roads identifier="A72" length="69.0"/> - </roadnetworks> + <roadnetworks identifier="LC" length="69.0" source="//@locations.2" target="//@locations.1"/> <locations name="Dresden"> <pois xsi:type="transportation_network:RestStop"/> <pois xsi:type="transportation_network:GasStation"> @@ -21,10 +19,8 @@ <fueltypes>DIESEL</fueltypes> <fueltypes>ELECTRICITY</fueltypes> </pois> - <pois xsi:type="transportation_network:CompanyWarehouse" name="A1"/> </locations> <locations name="Chemnitz"> - <pois xsi:type="transportation_network:CustomerWarehouse" name="OG-01" customerName="Office GmbH"/> <pois xsi:type="transportation_network:Other" name="Owner`s Home" type="Private House"/> </locations> <locations name="Leipzig"> @@ -38,9 +34,9 @@ <fueltypes>DIESEL</fueltypes> </pois> </locations> - <routes identifier="R1" networkList="//@roadnetworks.1 //@roadnetworks.3" start="//@locations.0" end="//@locations.1"> - <vehicle identifier="V03" location="//@locations.0" model="eTruck" fuelCapacity="150.0"/> - </routes> - <carPark identifier="V01" location="//@locations.0" model="eTruck" fuelCapacity="150.0"/> - <carPark identifier="V02" location="//@locations.2" model="eTruck" fuelCapacity="150.0"/> + <locations name="IsolatedLocation"/> + <routes identifier="R1" start="//@locations.0" end="//@locations.1"/> + <carPark identifier="V02" location="//@locations.2" model="eTruck" fuelCapacity="-150.0"/> + <carPark identifier="V03" location="//@locations.0" model="eTruck" fuelCapacity="150.0"/> + <carPark identifier="" location="//@locations.0" model="eTruck" fuelCapacity="150.0"/> </transportation_network:TransportationNetwork> diff --git a/stgroup.mdsd.transportation_network.test/.gitattributes b/stgroup.mdsd.transportation_network.test/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..f91f64602e6c6d892d70d71f4fc7a1bd943e23f3 --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/.gitattributes @@ -0,0 +1,12 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Binary files should be left untouched +*.jar binary + diff --git a/stgroup.mdsd.transportation_network.test/.gitignore b/stgroup.mdsd.transportation_network.test/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1b6985c0094c8e3db5f1c6e2c4d66b82f325284f --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/.gitignore @@ -0,0 +1,5 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build diff --git a/stgroup.mdsd.transportation_network.test/README.md b/stgroup.mdsd.transportation_network.test/README.md new file mode 100644 index 0000000000000000000000000000000000000000..51ec64cd0230d001c63d90db7461cec056964a07 --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/README.md @@ -0,0 +1,24 @@ +# Epsilon Gradle Example for the Transportation Network Example + +This repository gives a simple working example of the usage of EMF models (model transformation and unit testing) outside the direct eclipse ecosystem. + +The example uses [Epsilon](https://eclipse.dev/epsilon/) and [Gradle](https://gradle.org/) to create a simple CI pipeline. + +The goal is to give interested students a base project to build their own MDSD pipeline. + +## Usage + +The example pipeline has three Parts. + +1. Create a copy of the original model with: + ``` + ./gradlew copyOriginalModel + ``` +2. Transform/Rewrite the copy with + ``` + ./gradlew transform + ``` +3. Test if the transformation was running by comparing the original model with the rewritten copy. Run the tests with: + ``` + ./gradlew test + ``` \ No newline at end of file diff --git a/stgroup.mdsd.transportation_network.test/build.gradle b/stgroup.mdsd.transportation_network.test/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..017e5f909df2152b98dbbd21ca696180c500ba7c --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/build.gradle @@ -0,0 +1,98 @@ +configurations { + epsilon +} + +repositories { + mavenCentral() +} + +dependencies { + epsilon 'org.eclipse.epsilon:org.eclipse.epsilon.workflow:2.5.0' + epsilon 'org.eclipse.epsilon:org.eclipse.epsilon.workflow.emf:2.5.0' + epsilon 'org.eclipse.epsilon:org.eclipse.epsilon.eunit.cmp.emf:2.5.0' +} + +task setupEpsilonTasks { + // Set up the core Epsilon tasks + ant.taskdef(resource: 'org/eclipse/epsilon/workflow/tasks/tasks.xml', + classpath: configurations.epsilon.asPath, loaderref: 'epsilon') + // Set up the Epsilon EMF tasks + ant.taskdef(resource: 'org/eclipse/epsilon/workflow/tasks/emf/tasks.xml', + classpath: configurations.epsilon.asPath, loaderref: 'epsilon') + // Set logging level to info so that EOL's println() is not suppressed + ant.lifecycleLogLevel = 'INFO' +} + +task copyOriginalModel(type: Copy) { + from file(dir_model + 'saxony_original.transportation_network') + into dir_model + rename 'saxony_original','saxony' +} + +task register_source { + dependsOn setupEpsilonTasks, copyOriginalModel + + doFirst{ + // Load the EMF model + ant.'epsilon.emf.loadModel'( name: 'source', + modelfile: dir_model + 'saxony.transportation_network', + metamodelfile: dir_metamodel + 'transportation_network.ecore', + read: "true", + store: "true") + } +} + +task register_target { + dependsOn setupEpsilonTasks + + doFirst{ + // Load the EMF model + ant.'epsilon.emf.loadModel'( name: 'target', + modelfile: dir_model + 'saxony_t.transportation_network', + metamodelfile: dir_metamodel + 'transportation_network.ecore', + read: "false", + store: "false") + } +} + +task transform { + dependsOn register_source, register_target + + doFirst{ + ant.'epsilon.etl'(src: dir_script + "rewrite.etl"){ + model(ref: "source", alias: "source") + model(ref: "target", alias: "target") + } + } + + doLast{ + ant.'epsilon.disposeModel'(model: "source") + ant.'epsilon.disposeModel'(model: "target") + } + +} + +task test{ + dependsOn setupEpsilonTasks + + doFirst{ + ant.'epsilon.eunit'(src: dir_script + "test.eunit", failOnErrors: false, toDir: "./report/", report: "yes"){ + modelTasks() { + ant.'epsilon.emf.loadModel'( name: 'transformed', + modelfile: dir_model + 'saxony.transportation_network', + metamodelfile: dir_metamodel + 'transportation_network.ecore', + read: "true", + store: "false") + ant.'epsilon.emf.loadModel'( name: 'original', + modelfile: dir_model + 'saxony_original.transportation_network', + metamodelfile: dir_metamodel + 'transportation_network.ecore', + read: "true", + store: "false") + } + comparators() { + comparator(classname: 'org.eclipse.epsilon.eunit.cmp.emf.EMFModelComparator') + } + } + } + +} \ No newline at end of file diff --git a/stgroup.mdsd.transportation_network.test/gradle.properties b/stgroup.mdsd.transportation_network.test/gradle.properties new file mode 100644 index 0000000000000000000000000000000000000000..59f0f28f877248cddfa62ffb5bf8c0f9efe7a4c2 --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/gradle.properties @@ -0,0 +1,6 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +dir_metamodel =./metamodel/ +dir_script =./scripts/ +dir_model =./model/ \ No newline at end of file diff --git a/stgroup.mdsd.transportation_network.test/gradle/libs.versions.toml b/stgroup.mdsd.transportation_network.test/gradle/libs.versions.toml new file mode 100644 index 0000000000000000000000000000000000000000..4ac3234a6a7c3b47be8e41cd0167ea6aed456891 --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/gradle/libs.versions.toml @@ -0,0 +1,2 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format diff --git a/stgroup.mdsd.transportation_network.test/gradle/wrapper/gradle-wrapper.jar b/stgroup.mdsd.transportation_network.test/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..a4b76b9530d66f5e68d973ea569d8e19de379189 Binary files /dev/null and b/stgroup.mdsd.transportation_network.test/gradle/wrapper/gradle-wrapper.jar differ diff --git a/stgroup.mdsd.transportation_network.test/gradle/wrapper/gradle-wrapper.properties b/stgroup.mdsd.transportation_network.test/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..e2847c820046b34569292608a035c8ffc83be95a --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/stgroup.mdsd.transportation_network.test/gradlew b/stgroup.mdsd.transportation_network.test/gradlew new file mode 100644 index 0000000000000000000000000000000000000000..f5feea6d6b116baaca5a2642d4d9fa1f47d574a7 --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/stgroup.mdsd.transportation_network.test/gradlew.bat b/stgroup.mdsd.transportation_network.test/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..9d21a21834d5195c278ba17baec3115b2aaab06e --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/stgroup.mdsd.transportation_network.test/metamodel/transportation_network.ecore b/stgroup.mdsd.transportation_network.test/metamodel/transportation_network.ecore new file mode 100644 index 0000000000000000000000000000000000000000..43d35f6146ee13f200cef98075e254b9f8ee5fe8 --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/metamodel/transportation_network.ecore @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="transportation_network" nsURI="http://www.stgroup.org/transportation_network" + nsPrefix="transportation_network"> + <eClassifiers xsi:type="ecore:EClass" name="TransportationNetwork" eSuperTypes="#//IdentifiableElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="roadnetworks" upperBound="-1" + eType="#//RoadNetwork" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="locations" upperBound="-1" + eType="#//Location" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="routes" upperBound="-1" + eType="#//Route" containment="true"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="carPark" upperBound="-1" + eType="#//Vehicle" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Location" eSuperTypes="#//NamedElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="pois" upperBound="-1" eType="#//PointOfInterest" + containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RoadNetwork" eSuperTypes="#//IdentifiableElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="isDirected" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EBoolean" defaultValueLiteral="false"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="length" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" + defaultValueLiteral="-1.0"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="source" lowerBound="1" + eType="#//Location"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="target" lowerBound="1" + eType="#//Location"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="roads" upperBound="-1" + eType="#//Road" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Road" eSuperTypes="#//IdentifiableElement"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="length" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble" + defaultValueLiteral="0.0"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="IdentifiableElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="identifier" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="PointOfInterest" abstract="true" eSuperTypes="#//NamedElement"/> + <eClassifiers xsi:type="ecore:EClass" name="NamedElement" abstract="true"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="RestStop" eSuperTypes="#//PointOfInterest"/> + <eClassifiers xsi:type="ecore:EClass" name="GasStation" eSuperTypes="#//PointOfInterest"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fueltypes" lowerBound="1" + upperBound="-1" eType="#//FuelType"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EEnum" name="FuelType"> + <eLiterals name="PETROL"/> + <eLiterals name="DIESEL" value="1"/> + <eLiterals name="ELECTRICITY" value="2"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Warehouse" abstract="true" eSuperTypes="#//PointOfInterest"/> + <eClassifiers xsi:type="ecore:EClass" name="Other" eSuperTypes="#//PointOfInterest"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="type" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="CompanyWarehouse" eSuperTypes="#//Warehouse"/> + <eClassifiers xsi:type="ecore:EClass" name="CustomerWarehouse" eSuperTypes="#//Warehouse"> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="customerName" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Route" eSuperTypes="#//IdentifiableElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="networkList" upperBound="-1" + eType="#//RoadNetwork"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="start" eType="#//Location"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="end" eType="#//Location"/> + <eStructuralFeatures xsi:type="ecore:EReference" name="vehicle" lowerBound="1" + eType="#//Vehicle" containment="true"/> + </eClassifiers> + <eClassifiers xsi:type="ecore:EClass" name="Vehicle" eSuperTypes="#//IdentifiableElement"> + <eStructuralFeatures xsi:type="ecore:EReference" name="location" eType="#//Location"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="model" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fuelCapacity" lowerBound="1" + eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"/> + <eStructuralFeatures xsi:type="ecore:EAttribute" name="fuelType" lowerBound="1" + eType="#//FuelType" defaultValueLiteral="ELECTRICITY"/> + </eClassifiers> +</ecore:EPackage> diff --git a/stgroup.mdsd.transportation_network.test/model/saxony_original.transportation_network b/stgroup.mdsd.transportation_network.test/model/saxony_original.transportation_network new file mode 100644 index 0000000000000000000000000000000000000000..3cdd3ef51096120ffbb716a8090b4c4814313cae --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/model/saxony_original.transportation_network @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<transportation_network:TransportationNetwork xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:transportation_network="http://www.stgroup.org/transportation_network" identifier="saxony"> + <roadnetworks identifier="DG" length="95.0" source="//@locations.0" target="//@locations.3"> + <roads identifier="A4" length="95.0"/> + </roadnetworks> + <roadnetworks identifier="DL" length="102.0" source="//@locations.0" target="//@locations.2"> + <roads identifier="A4" length="20.0"/> + <roads identifier="A14" length="67.0"/> + <roads identifier="A38" length="15.0"/> + </roadnetworks> + <roadnetworks identifier="DC" length="64.0" source="//@locations.0" target="//@locations.1"> + <roads identifier="A4" length="64.0"/> + </roadnetworks> + <roadnetworks identifier="LC" length="69.0" source="//@locations.2" target="//@locations.1"> + <roads identifier="A72" length="69.0"/> + </roadnetworks> + <locations name="Dresden"> + <pois xsi:type="transportation_network:RestStop"/> + <pois xsi:type="transportation_network:GasStation"> + <fueltypes>PETROL</fueltypes> + <fueltypes>DIESEL</fueltypes> + <fueltypes>ELECTRICITY</fueltypes> + </pois> + </locations> + <locations name="Chemnitz"> + <pois xsi:type="transportation_network:CustomerWarehouse" name="OG-01" customerName="Office GmbH"/> + <pois xsi:type="transportation_network:Other" name="Owner`s Home" type="Private House"/> + </locations> + <locations name="Leipzig"> + <pois xsi:type="transportation_network:CustomerWarehouse" name="DA-01" customerName="Delivery AG"/> + <pois xsi:type="transportation_network:RestStop"/> + </locations> + <locations name="Görlitz"> + <pois xsi:type="transportation_network:RestStop"/> + <pois xsi:type="transportation_network:GasStation"> + <fueltypes>PETROL</fueltypes> + <fueltypes>DIESEL</fueltypes> + </pois> + </locations> + <routes identifier="R1" networkList="//@roadnetworks.1 //@roadnetworks.3" start="//@locations.0" end="//@locations.1"> + <vehicle identifier="V01" location="//@locations.0" model="eTruck" fuelCapacity="150.0"/> + </routes> + <carPark identifier="V02" location="//@locations.2" model="eTruck" fuelCapacity="150.0"/> + <carPark identifier="V03" location="//@locations.0" model="eTruck" fuelCapacity="150.0"/> +</transportation_network:TransportationNetwork> diff --git a/stgroup.mdsd.transportation_network.test/report/.gitkeep b/stgroup.mdsd.transportation_network.test/report/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/stgroup.mdsd.transportation_network.test/scripts/rewrite.etl b/stgroup.mdsd.transportation_network.test/scripts/rewrite.etl new file mode 100644 index 0000000000000000000000000000000000000000..eca5cfe960897bad55365f884f5761479494f49e --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/scripts/rewrite.etl @@ -0,0 +1,98 @@ +/* +* Example of extending by rewriting a model. +* Here: Splitting a road network if the network +* use multiple roads. +*/ + +/* +* Normal configuration: +* Source model: Read=true, Write=true +* Target model: Read=false, Write=false +*/ + +pre { + "Model Extending/Rewrite Example".println(); + "-------------------------------------".println(); + "Start of transformation...".println(); +} + +post { + "... End of transformation.".println(); +} + +/* +* Option 1: Have a rule for the model elements which need to be extended. +* Write the extended elements in your source model. +*/ + +rule SplitRoadNetworks + transform s : source!Road + to l : source!Location, + rn : source!RoadNetwork + { + + var originalRN:source!RoadNetwork = s.eContainer(); + var network:source!TransportationNetwork = originalRN.eContainer(); + + network.roadnetworks.add(rn); + network.locations.add(l); + + rn.identifier = originalRN.identifier + "/" + originalRN.roads.size(); + rn.roads.add(s); + rn.length = s.length; + rn.source = originalRN.source; + if (originalRN.roads.isEmpty()){ + rn.target = originalRN.target; + delete l; + } else{ + rn.target = l; + originalRN.source = l; + } + +} + + +rule deleteEmptyRoadNetworks + transform s : source!RoadNetwork + to t : target!EObject + { + + if(s.roads.isEmpty()){ + delete s; + } +} + + +/* +* Optional Part for merging Road Networks with the help of an operation +*/ +rule mergeRoadNetworks + transform s : source!TransportationNetwork + to t : target!EObject{ + + for (r in s.roadnetworks) { + var toBeMergeRNs = r.RNtoMerge(); + if(not toBeMergeRNs.isEmpty()){ + toBeMergeRNs.add(r); + var minRoad = toBeMergeRNs.sortBy(rn | rn.roads.first().length).first(); + toBeMergeRNs.remove(minRoad); + for (mergingRN in toBeMergeRNs) { + mergingRN.source = minRoad.target; + mergingRN.roads.first().length -= minRoad.roads.first().length; + mergingRN.length = mergingRN.roads.first().length; + } + } + } +} + +operation source!RoadNetwork RNtoMerge() : Set { + if(self.eContainer() <> null){ + return self.eContainer().roadnetworks.select(rn | rn.source.equals(self.source) + and not (rn == self)) + .select(rn | (rn.roads.size() == 1) + and (rn.roads.first().identifier == self.roads.first().identifier)); + } else{ + return new Set; + } + +} diff --git a/stgroup.mdsd.transportation_network.test/scripts/test.eunit b/stgroup.mdsd.transportation_network.test/scripts/test.eunit new file mode 100644 index 0000000000000000000000000000000000000000..99401b04894145d04fa55f5924f41d2ba2920d9c --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/scripts/test.eunit @@ -0,0 +1,20 @@ +@Test +operation testModelChanged() { + assertNotEqualModels("The model should have be changed by a transformation!", "transformed", "original"); +} + +@Test +operation testNumberOfLocations() { + var numLocNew:Integer = transformed!Location.all().size(); + var numLocBefore:Integer = original!Location.all().size(); + assertEquals("The original Model should have 4 Locations. It has " + numLocBefore, numLocBefore, 4); + assertEquals("The transformed Model should have 6 Locations. It has " + numLocNew, numLocNew, 6); +} + +@Test +operation RoadNetworksShouldOnlyHaveOneRoad() { + for(rn in transformed!RoadNetwork.all()){ + var numRoads:Integer = rn.roads.size(); + assertEquals(rn.identifier + " should have 1 road, but it has " + numRoads, numRoads, 1); + } +} \ No newline at end of file diff --git a/stgroup.mdsd.transportation_network.test/settings.gradle b/stgroup.mdsd.transportation_network.test/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..29655b9e698b6c0dd7645af493be38373753b8d7 --- /dev/null +++ b/stgroup.mdsd.transportation_network.test/settings.gradle @@ -0,0 +1,9 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.11.1/userguide/multi_project_builds.html in the Gradle documentation. + * This project uses @Incubating APIs which are subject to change. + */ + +rootProject.name = 'EpsilonGradleExample' diff --git a/stgroup.mdsd.transportation_network.validation/.project b/stgroup.mdsd.transportation_network.validation/.project new file mode 100644 index 0000000000000000000000000000000000000000..d45f5a3c036ed2d2efdac083b8308a4a8f7f39fa --- /dev/null +++ b/stgroup.mdsd.transportation_network.validation/.project @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>stgroup.mdsd.transportation_network.validation</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + </buildSpec> + <natures> + </natures> +</projectDescription> diff --git a/stgroup.mdsd.transportation_network.validation/validation.evl b/stgroup.mdsd.transportation_network.validation/validation.evl new file mode 100644 index 0000000000000000000000000000000000000000..db955f9666461805aeb3bc5ee7aeb0f3d62acc8b --- /dev/null +++ b/stgroup.mdsd.transportation_network.validation/validation.evl @@ -0,0 +1,70 @@ +pre{ + "Model Validation Example".println(); + "-------------------------------------".println(); + "Start of validation...".println(); + +} + +post{ + "... End of validation.".println(); +} + +context source!NamedElement { + + critique ElementWithoutName{ + + guard: not self.isKindOf(source!Location) + + check: self.name <> null and not self.name.isEmpty() and not self.name.isBlank() + + message: "Name of an element should not be empty! Element: " + self + + fix { + title: "Give a default name" + do { + self.name = self.type().name + source!NamedElement.all().size(); + } + } + } +} + +context source!IdentifiableElement{ + + critique ElementWithoutId{ + + check: self.identifier <> null and not self.identifier.isEmpty() and not self.identifier.isBlank() + + message { + self.identifier = self.type().name + source!IdentifiableElement.all().size(); + return "Id of an element should not be empty! Default id was set! Element: " + self; + } + + } + +} + +context source!RoadNetwork{ + + constraint RoadNetworkWithoutSourceOrTarget{ + + check{ + var resultSource:Boolean = self.source <> null; + var resultTarget:Boolean = self.target <> null; + return (resultSource and resultTarget); + } + + message { + var returnMsg = "Element: " + self; + if(not resultTarget){ + returnMsg = "Target of a RoadNetwork must be set! " + returnMsg; + } + if(not resultSource){ + returnMsg = "Source of a RoadNetwork must be set! " + returnMsg; + } + return returnMsg; + } + + } + +} +