From 54263295729f290ea932cdad7e2f45619a3bb5b8 Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Mon, 25 Nov 2024 16:50:59 +0100 Subject: [PATCH 01/10] Clean up files and apply formatting --- .github/workflows/deploy.yml | 16 +- .github/workflows/main_build.yml | 17 +- .github/workflows/style.yml | 26 + .github/workflows/version.yml | 13 +- .gitignore | 3 + .../pom.xml | 66 +- de.darmstadt.tu.crossing.CrySL.ide/.classpath | 9 - de.darmstadt.tu.crossing.CrySL.ide/pom.xml | 140 ++-- .../pom.xml | 114 +-- de.darmstadt.tu.crossing.CrySL.target/pom.xml | 61 +- .../.classpath | 21 - de.darmstadt.tu.crossing.CrySL.tests/pom.xml | 93 +-- .../tu/crossing/tests/CrySLParsingTest.xtend | 12 - .../.classpath | 9 - de.darmstadt.tu.crossing.CrySL.ui/.classpath | 9 - de.darmstadt.tu.crossing.CrySL.ui/pom.xml | 118 +-- de.darmstadt.tu.crossing.CrySL/.classpath | 9 - de.darmstadt.tu.crossing.CrySL/pom.xml | 373 ++++----- pom.xml | 778 ++++++++++-------- 19 files changed, 936 insertions(+), 951 deletions(-) create mode 100644 .github/workflows/style.yml delete mode 100644 de.darmstadt.tu.crossing.CrySL.ide/.classpath delete mode 100644 de.darmstadt.tu.crossing.CrySL.tests/.classpath delete mode 100644 de.darmstadt.tu.crossing.CrySL.ui.tests/.classpath delete mode 100644 de.darmstadt.tu.crossing.CrySL.ui/.classpath delete mode 100644 de.darmstadt.tu.crossing.CrySL/.classpath diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fceb7012..a470d022 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,18 +4,15 @@ on: [workflow_dispatch] jobs: deployment: - strategy: - matrix: - os: [ ubuntu-latest ] - runs-on: ${{ matrix.os }} + runs-on: ubuntu-latest environment: Deploy - name: Deployment in ${{ matrix.os }} + name: Deploy CrySL steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Sets up Java version - name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'adopt' java-package: 'jdk' @@ -25,11 +22,6 @@ jobs: server-password: OSSRH_PASSWORD # Env var that holds your OSSRH user pw gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} # Substituted with the value stored in the referenced secret gpg-passphrase: SIGN_KEY_PASS # Env var that holds the key's passphrase - # Sets up Maven version - - name: Set up Maven - uses: stCarolas/setup-maven@v4.5 - with: - maven-version: 3.6.3 - name: Build & Deploy CrySL run: mvn -B -U clean deploy -Pdeployment env: diff --git a/.github/workflows/main_build.yml b/.github/workflows/main_build.yml index d8c4d14f..0524ad40 100644 --- a/.github/workflows/main_build.yml +++ b/.github/workflows/main_build.yml @@ -1,6 +1,12 @@ name: CrySL build -on: [push, pull_request] +on: + push: + branches-ignore: + - master + - develop + pull_request: + types: [opened, reopened] jobs: # Builds the project in windows, ubuntu and macos @@ -12,19 +18,14 @@ jobs: name: Project build in ${{ matrix.os }} steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Sets up Java version - name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'adopt' java-package: jdk java-version: '11' - # Sets up Maven version - - name: Set up Maven - uses: stCarolas/setup-maven@v4.5 - with: - maven-version: 3.8.6 # Restores Maven dependecies - name: Restore local Maven repository uses: actions/cache@v3 diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml new file mode 100644 index 00000000..894f1638 --- /dev/null +++ b/.github/workflows/style.yml @@ -0,0 +1,26 @@ +name: Spotless Style Check +description: Check the formatting. Use "mvn spotless:apply" to format the code. + +on: + push: + branches-ignore: + - master + - develop + +jobs: + check-formatting: + runs-on: ubuntu-latest + name: Check style + steps: + - name: Checkout source code + uses: actions/checkout@v4 + # Restores Maven dependecies + - name: Restore local Maven repository + uses: actions/cache@v4 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Run spotless checks + run: mvn spotless:check diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index eff20001..631fc1c1 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -15,21 +15,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # Sets up Java version - name: Set up Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'adopt' java-package: jdk java-version: '11' - # Sets up Maven version - - name: Set up Maven - uses: stCarolas/setup-maven@v4.5 - with: - maven-version: 3.6.3 # Semantic versioning - name: Semantic versioning id: versioning @@ -85,7 +80,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 # Semantic versioning @@ -107,4 +102,4 @@ jobs: git config --global user.email "${{ github.actor }}@users.noreply.github.com" git config --global user.name "${{ github.actor }}" git tag -a ${{ steps.versioning.outputs.version }} -m "CrySL version ${{ steps.versioning.outputs.version }}" - git push origin ${{ steps.versioning.outputs.version }} \ No newline at end of file + git push origin ${{ steps.versioning.outputs.version }} diff --git a/.gitignore b/.gitignore index c015d8e2..5066ea49 100644 --- a/.gitignore +++ b/.gitignore @@ -121,6 +121,7 @@ cmake-build-*/ # IntelliJ out/ +.idea/ # mpeltonen/sbt-idea plugin .idea_modules/ @@ -193,6 +194,8 @@ buildNumber.properties .mvn/timing.properties # https://github.com/takari/maven-wrapper#usage-without-binary-jar .mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml +**.iml # Eclipse m2e generated files # Eclipse Core diff --git a/de.darmstadt.tu.crossing.CrySL.feature/pom.xml b/de.darmstadt.tu.crossing.CrySL.feature/pom.xml index 09f8392d..66b1db6f 100644 --- a/de.darmstadt.tu.crossing.CrySL.feature/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL.feature/pom.xml @@ -1,34 +1,34 @@ - - 4.0.0 - de.darmstadt.tu.crossing.CrySL.feature - eclipse-feature - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.parent - 3.0.2 - + + + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + + de.darmstadt.tu.crossing.CrySL.feature + eclipse-feature - CrySL-Feature - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL - \ No newline at end of file + CrySL-Feature + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + + diff --git a/de.darmstadt.tu.crossing.CrySL.ide/.classpath b/de.darmstadt.tu.crossing.CrySL.ide/.classpath deleted file mode 100644 index 0377d772..00000000 --- a/de.darmstadt.tu.crossing.CrySL.ide/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/de.darmstadt.tu.crossing.CrySL.ide/pom.xml b/de.darmstadt.tu.crossing.CrySL.ide/pom.xml index 31d1e3e9..601d2133 100644 --- a/de.darmstadt.tu.crossing.CrySL.ide/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL.ide/pom.xml @@ -1,73 +1,75 @@ - - 4.0.0 - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.parent - 3.0.2 - - de.darmstadt.tu.crossing.CrySL.ide - eclipse-plugin + + + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + + de.darmstadt.tu.crossing.CrySL.ide + eclipse-plugin - CrySL-IDE - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL + CrySL-IDE + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + - - - - org.apache.maven.plugins - maven-clean-plugin - 3.4.0 - - - gen-clean - clean - - - - - org.eclipse.xtend - xtend-maven-plugin - - - maven-assembly-plugin - - - make-assembly - package - - single - - - - - - jar-with-dependencies - - build - - - - + + + + org.apache.maven.plugins + maven-clean-plugin + 3.4.0 + + + gen-clean + clean + + + + + org.eclipse.xtend + xtend-maven-plugin + + + maven-assembly-plugin + + + jar-with-dependencies + + build + + + + make-assembly + + + single + + + package + + + + + diff --git a/de.darmstadt.tu.crossing.CrySL.repository/pom.xml b/de.darmstadt.tu.crossing.CrySL.repository/pom.xml index 52fbedc0..1798e27b 100644 --- a/de.darmstadt.tu.crossing.CrySL.repository/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL.repository/pom.xml @@ -1,59 +1,59 @@ - - 4.0.0 - de.darmstadt.tu.crossing.CrySL.repository - eclipse-repository - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.parent - 3.0.2 - + + + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + + de.darmstadt.tu.crossing.CrySL.repository + eclipse-repository - CrySL-Repository - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL + CrySL-Repository + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + - - - - org.eclipse.tycho - tycho-p2-repository-plugin - ${tycho-version} - - true - - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.1 - - - attach-source - - - - - - - \ No newline at end of file + + + + org.eclipse.tycho + tycho-p2-repository-plugin + ${tycho-version} + + true + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-source + + + + + + + diff --git a/de.darmstadt.tu.crossing.CrySL.target/pom.xml b/de.darmstadt.tu.crossing.CrySL.target/pom.xml index c666231c..faddeb41 100644 --- a/de.darmstadt.tu.crossing.CrySL.target/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL.target/pom.xml @@ -1,34 +1,35 @@ + - 4.0.0 - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.parent - 3.0.2 - - de.darmstadt.tu.crossing.CrySL.target - eclipse-target-definition + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + + de.darmstadt.tu.crossing.CrySL.target + eclipse-target-definition - CrySL-Target - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL + CrySL-Target + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + diff --git a/de.darmstadt.tu.crossing.CrySL.tests/.classpath b/de.darmstadt.tu.crossing.CrySL.tests/.classpath deleted file mode 100644 index 66bf086e..00000000 --- a/de.darmstadt.tu.crossing.CrySL.tests/.classpath +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/de.darmstadt.tu.crossing.CrySL.tests/pom.xml b/de.darmstadt.tu.crossing.CrySL.tests/pom.xml index 33e241e8..43db2c2a 100644 --- a/de.darmstadt.tu.crossing.CrySL.tests/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL.tests/pom.xml @@ -1,51 +1,52 @@ + - 4.0.0 - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.parent - 3.0.2 - - de.darmstadt.tu.crossing.CrySL.tests - eclipse-test-plugin + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + + de.darmstadt.tu.crossing.CrySL.tests + eclipse-test-plugin - CrySL-Tests - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL + CrySL-Tests + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + - - - - org.eclipse.xtend - xtend-maven-plugin - - - org.eclipse.tycho - tycho-surefire-plugin - ${tycho-version} - - false - - - - + + + + org.eclipse.xtend + xtend-maven-plugin + + + org.eclipse.tycho + tycho-surefire-plugin + ${tycho-version} + + false + + + + diff --git a/de.darmstadt.tu.crossing.CrySL.tests/src/de/darmstadt/tu/crossing/tests/CrySLParsingTest.xtend b/de.darmstadt.tu.crossing.CrySL.tests/src/de/darmstadt/tu/crossing/tests/CrySLParsingTest.xtend index 7474188b..6b31e09a 100644 --- a/de.darmstadt.tu.crossing.CrySL.tests/src/de/darmstadt/tu/crossing/tests/CrySLParsingTest.xtend +++ b/de.darmstadt.tu.crossing.CrySL.tests/src/de/darmstadt/tu/crossing/tests/CrySLParsingTest.xtend @@ -1,17 +1,5 @@ -/* - * generated by Xtext 2.10.0 - */ package de.darmstadt.tu.crossing.tests -import com.google.inject.Inject -import de.darmstadt.tu.crossing.crySL.Domainmodel -import org.eclipse.xtext.junit4.InjectWith -import org.eclipse.xtext.junit4.XtextRunner -import org.eclipse.xtext.junit4.util.ParseHelper -import org.junit.Assert -import org.junit.Test -import org.junit.runner.RunWith - /*@RunWith(XtextRunner) class CrySLParsingTest{ diff --git a/de.darmstadt.tu.crossing.CrySL.ui.tests/.classpath b/de.darmstadt.tu.crossing.CrySL.ui.tests/.classpath deleted file mode 100644 index 8d26fa59..00000000 --- a/de.darmstadt.tu.crossing.CrySL.ui.tests/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/de.darmstadt.tu.crossing.CrySL.ui/.classpath b/de.darmstadt.tu.crossing.CrySL.ui/.classpath deleted file mode 100644 index 0377d772..00000000 --- a/de.darmstadt.tu.crossing.CrySL.ui/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/de.darmstadt.tu.crossing.CrySL.ui/pom.xml b/de.darmstadt.tu.crossing.CrySL.ui/pom.xml index 33c91fc7..870a0ce4 100644 --- a/de.darmstadt.tu.crossing.CrySL.ui/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL.ui/pom.xml @@ -1,62 +1,64 @@ - - 4.0.0 - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.parent - 3.0.2 - - de.darmstadt.tu.crossing.CrySL.ui - eclipse-plugin + + + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + + de.darmstadt.tu.crossing.CrySL.ui + eclipse-plugin - CrySL-UI - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL + CrySL-UI + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + - - - - org.eclipse.xtend - xtend-maven-plugin - - - maven-assembly-plugin - - - make-assembly - package - - single - - - - - - jar-with-dependencies - - build - - - - + + + + org.eclipse.xtend + xtend-maven-plugin + + + maven-assembly-plugin + + + jar-with-dependencies + + build + + + + make-assembly + + + single + + + package + + + + + diff --git a/de.darmstadt.tu.crossing.CrySL/.classpath b/de.darmstadt.tu.crossing.CrySL/.classpath deleted file mode 100644 index 0377d772..00000000 --- a/de.darmstadt.tu.crossing.CrySL/.classpath +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/de.darmstadt.tu.crossing.CrySL/pom.xml b/de.darmstadt.tu.crossing.CrySL/pom.xml index bb94a54b..80fae9b5 100644 --- a/de.darmstadt.tu.crossing.CrySL/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL/pom.xml @@ -1,199 +1,180 @@ - - 4.0.0 - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.parent - 3.0.2 - - de.darmstadt.tu.crossing.CrySL - eclipse-plugin + + + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + + de.darmstadt.tu.crossing.CrySL + eclipse-plugin - CrySL - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL + CrySL + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.codehaus.mojo + exec-maven-plugin + [1.2.1,) + + java + + + + + + + + + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.4.1 + + org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher + + /${project.basedir}/src/de/darmstadt/tu/crossing/GenerateCrySL.mwe2 + -p + rootPath=/${project.basedir}/.. + + compile + true + false + + + + + org.eclipse.emf + org.eclipse.emf.mwe2.launch + ${emfVersion} + + + org.eclipse.xtext + org.eclipse.xtext.xtext.generator + ${xtextVersion} + + + + org.eclipse.emf + org.eclipse.emf.mwe2.runtime + + + org.eclipse.platform + org.eclipse.equinox.common + + + org.eclipse.platform + org.eclipse.core.runtime + + + + + org.eclipse.xtext + org.eclipse.xtext.xbase + ${xtextVersion} + + + org.eclipse.emf + org.eclipse.emf.mwe2.runtime + ${emfVersion} + + + + + mwe2Launcher + + java + + generate-sources + + + + + org.eclipse.xtend + xtend-maven-plugin + + + maven-assembly-plugin + + + jar-with-dependencies + + build + + + + make-assembly + + + single + + + package + + + + + org.apache.maven.plugins + maven-clean-plugin + 3.4.0 + + + + ${basedir}/src-gen/ + + ** + + + + ${basedir}/xtend-gen/ + + ** + + + + ${basedir}/model/generated/ + + + + + + - - - - org.codehaus.mojo - exec-maven-plugin - 3.4.1 - - - mwe2Launcher - generate-sources - - java - - - - - org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher - - /${project.basedir}/src/de/darmstadt/tu/crossing/GenerateCrySL.mwe2 - -p - rootPath=/${project.basedir}/.. - - compile - true - false - - - - org.eclipse.emf - org.eclipse.emf.mwe2.launch - ${emfVersion} - - - org.eclipse.xtext - org.eclipse.xtext.xtext.generator - ${xtextVersion} - - - org.eclipse.emf - org.eclipse.emf.mwe2.runtime - - - org.eclipse.platform - org.eclipse.equinox.common - - - org.eclipse.platform - org.eclipse.core.runtime - - - - - org.eclipse.xtext - org.eclipse.xtext.xbase - ${xtextVersion} - - - org.eclipse.emf - org.eclipse.emf.mwe2.runtime - ${emfVersion} - - - - - org.eclipse.xtend - xtend-maven-plugin - - - maven-assembly-plugin - - - make-assembly - package - - single - - - - - - jar-with-dependencies - - build - - - - org.apache.maven.plugins - maven-clean-plugin - 3.4.0 - - - - ${basedir}/../de.darmstadt.tu.crossing.CrySL/src-gen/ - - **/* - - - - ${basedir}/../de.darmstadt.tu.crossing.CrySL.tests/src-gen/ - - **/* - - - - ${basedir}/../de.darmstadt.tu.crossing.CrySL.ide/src-gen/ - - **/* - - - - ${basedir}/../de.darmstadt.tu.crossing.CrySL.ui/src-gen/ - - **/* - - - - ${basedir}/../de.darmstadt.tu.crossing.CrySL.ui.tests/src-gen/ - - **/* - - - - ${basedir}/model/generated/ - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.codehaus.mojo - - - exec-maven-plugin - - - [1.2.1,) - - - java - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index a245ca5e..1a76fa51 100644 --- a/pom.xml +++ b/pom.xml @@ -1,373 +1,423 @@ - - 4.0.0 - de.darmstadt.tu.crossing.CrySL - 3.0.2 - de.darmstadt.tu.crossing.CrySL.parent - pom - - CrySL-Parent - - - Eclipse Public License - v2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - - - CogniCrypt - CogniCrypt - cognicrypt@eim.upb.de - - - - scm:git:git@github.com:CROSSINGTUD/CryptSL.git - scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git - https://github.com/CROSSINGTUD/CryptSL - - CrySL domain-specific language - https://github.com/CROSSINGTUD/CryptSL + + + 4.0.0 + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + pom - - 2.7.5 - 2.35.0 - 2.19.0 - UTF-8 - 1.8 - 1.8 - + CrySL-Parent + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.ide - de.darmstadt.tu.crossing.CrySL.ui - de.darmstadt.tu.crossing.CrySL.target - de.darmstadt.tu.crossing.CrySL.feature - de.darmstadt.tu.crossing.CrySL.repository - de.darmstadt.tu.crossing.CrySL.tests - + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.ide + de.darmstadt.tu.crossing.CrySL.ui + de.darmstadt.tu.crossing.CrySL.target + de.darmstadt.tu.crossing.CrySL.feature + de.darmstadt.tu.crossing.CrySL.repository + de.darmstadt.tu.crossing.CrySL.tests + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + - - - - deployment - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 - true - - ossrh - https://s01.oss.sonatype.org - true - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.2.7 - - - sign-artifacts - verify - - sign - - - - --pinentry-mode - loopback - - - - - - - - - + + + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + - - - - org.eclipse.tycho - tycho-maven-plugin - ${tycho-version} - true - - - org.eclipse.tycho - tycho-versions-plugin - ${tycho-version} - - - org.eclipse.tycho - target-platform-configuration - ${tycho-version} - - - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.target - ${project.version} - - - - - macosx - cocoa - x86_64 - - - win32 - win32 - x86_64 - - - linux - gtk - x86_64 - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.1 - - - attach-source - - jar - - - - - - org.apache.maven.plugins - maven-release-plugin - 3.1.1 - - @{project.version} - - - + + 2.7.5 + 2.35.0 + 2.19.0 + UTF-8 + 1.8 + 1.8 + - - - - - org.eclipse.xtend - xtend-maven-plugin - ${xtextVersion} - - - - compile - xtend-install-debug-info - testCompile - xtend-test-install-debug-info - - - - - xtend-gen - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - - org.apache.maven.plugins - - - maven-resources-plugin - - - [2.4.3,) - - - resources - testResources - - - - - - - - - - org.codehaus.mojo - - - build-helper-maven-plugin - - - [1.9.1,) - - - add-resource - add-source - add-test-resource - add-test-source - - - - - - - - - - org.eclipse.tycho - - - tycho-compiler-plugin - - - [0.23.1,) - - - compile - - - - - - - - - - org.eclipse.tycho - - - tycho-packaging-plugin - - - [0.23.1,) - - - build - build-aggregator - validate-id - validate-version - - - - - - - - - - - - org.eclipse.jdt - org.eclipse.jdt.core - 3.39.0 - - - org.eclipse.platform - org.eclipse.equinox.common - - - org.eclipse.platform - org.eclipse.core.runtime - - - - - org.eclipse.jdt - org.eclipse.jdt.compiler.apt - 1.4.300 - - - org.eclipse.platform - org.eclipse.equinox.common - - - org.eclipse.platform - org.eclipse.core.runtime - - - - - org.eclipse.jdt - org.eclipse.jdt.compiler.tool - 1.3.200 - - - org.eclipse.platform - org.eclipse.equinox.common - - - org.eclipse.platform - org.eclipse.core.runtime - - - - - org.eclipse.emf - org.eclipse.emf.codegen - 2.24.0 - - - - - - - org.eclipse.tycho - tycho-compiler-plugin - ${tycho-version} - - -err:-forbidden - - - - maven-assembly-plugin - - - make-assembly - package - - single - - - - - - jar-with-dependencies - - build - - - - - + + org.eclipse.xtend + xtend-maven-plugin + ${xtextVersion} + + xtend-gen + + + + + compile + xtend-install-debug-info + testCompile + xtend-test-install-debug-info + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-resources-plugin + [2.4.3,) + + resources + testResources + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + [1.9.1,) + + add-resource + add-source + add-test-resource + add-test-source + + + + + + + + + org.eclipse.tycho + tycho-compiler-plugin + [0.23.1,) + + compile + + + + + + + + + org.eclipse.tycho + tycho-packaging-plugin + [0.23.1,) + + build + build-aggregator + validate-id + validate-version + + + + + + + + + + + + org.eclipse.jdt + org.eclipse.jdt.core + 3.39.0 + + + org.eclipse.platform + org.eclipse.equinox.common + + + org.eclipse.platform + org.eclipse.core.runtime + + + + + org.eclipse.jdt + org.eclipse.jdt.compiler.apt + 1.4.300 + + + org.eclipse.platform + org.eclipse.equinox.common + + + org.eclipse.platform + org.eclipse.core.runtime + + + + + org.eclipse.jdt + org.eclipse.jdt.compiler.tool + 1.3.200 + + + org.eclipse.platform + org.eclipse.equinox.common + + + org.eclipse.platform + org.eclipse.core.runtime + + + + + org.eclipse.emf + org.eclipse.emf.codegen + 2.24.0 + + + + + + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho-version} + + -err:-forbidden + + + + maven-assembly-plugin + + + jar-with-dependencies + + build + + + + make-assembly + + + single + + + package + + + + + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho-version} + true + + + org.eclipse.tycho + tycho-versions-plugin + ${tycho-version} + + + org.eclipse.tycho + target-platform-configuration + ${tycho-version} + + + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.target + ${project.version} + + + + + macosx + cocoa + x86_64 + + + win32 + win32 + x86_64 + + + linux + gtk + x86_64 + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-source + + jar + + + + + + org.apache.maven.plugins + maven-release-plugin + 3.1.1 + + @{project.version} + + - - - ossrh - https://s01.oss.sonatype.org/content/repositories/snapshots/ - - - ossrh - https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ - - + + org.codehaus.mojo + flatten-maven-plugin + 1.6.0 + + true + ossrh + + + + flatten + + flatten + + process-resources + + + flatten.clean + + clean + + clean + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.43.0 + + + + pom.xml + de.darmstadt.tu.crossing.CrySL.feature/pom.xml + de.darmstadt.tu.crossing.CrySL.ide/pom.xml + de.darmstadt.tu.crossing.CrySL.repository/pom.xml + de.darmstadt.tu.crossing.CrySL.tests/pom.xml + de.darmstadt.tu.crossing.CrySL.ui/pom.xml + de.darmstadt.tu.crossing.CrySL/pom.xml + + + UTF-8 + true + 4 + false + true + false + + + + + 1.24.0 + + + + + + + + + + + + + apply + + + + + + + + + + + deployment + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.7.0 + true + + ossrh + https://s01.oss.sonatype.org + true + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.7 + + + sign-artifacts + + sign + + verify + + + --pinentry-mode + loopback + + + + + + + + + From 340214b913dc78e2004cd45fcc7315365a0f8fcc Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Tue, 26 Nov 2024 13:09:33 +0100 Subject: [PATCH 02/10] Add crysl components from CryptoAnalysis to this repository --- CryslParser/pom.xml | 92 +++ .../de/darmstadt/tu/crossing/CrySLParser.java | 192 ++++++ .../tu/crossing/parsing/CrySLModelReader.java | 650 ++++++++++++++++++ .../parsing/CrySLModelReaderClassPath.java | 135 ++++ .../tu/crossing/parsing/CrySLReaderUtils.java | 217 ++++++ .../tu/crossing/parsing/CryslException.java | 8 + .../tu/crossing/parsing/ExceptionsReader.java | 30 + .../parsing/StateMachineGraphBuilder.java | 258 +++++++ .../rule/CrySLArithmeticConstraint.java | 87 +++ .../rule/CrySLComparisonConstraint.java | 80 +++ .../tu/crossing/rule/CrySLCondPredicate.java | 52 ++ .../tu/crossing/rule/CrySLConstraint.java | 64 ++ .../tu/crossing/rule/CrySLException.java | 28 + .../rule/CrySLExceptionConstraint.java | 61 ++ .../crossing/rule/CrySLForbiddenMethod.java | 55 ++ .../tu/crossing/rule/CrySLLiteral.java | 6 + .../tu/crossing/rule/CrySLMethod.java | 121 ++++ .../tu/crossing/rule/CrySLObject.java | 64 ++ .../tu/crossing/rule/CrySLPredicate.java | 156 +++++ .../darmstadt/tu/crossing/rule/CrySLRule.java | 171 +++++ .../tu/crossing/rule/CrySLSplitter.java | 32 + .../crossing/rule/CrySLValueConstraint.java | 60 ++ .../tu/crossing/rule/FiniteStateMachine.java | 11 + .../rule/ICrySLPredicateParameter.java | 6 + .../tu/crossing/rule/ISLConstraint.java | 8 + .../tu/crossing/rule/StateMachineGraph.java | 189 +++++ .../rule/StateMachineGraphReader.java | 23 + .../darmstadt/tu/crossing/rule/StateNode.java | 100 +++ .../tu/crossing/rule/Transition.java | 11 + .../tu/crossing/rule/TransitionEdge.java | 74 ++ .../src/main/resources/plugin.properties | 2 + .../src/test/java/parser/CrySLParserTest.java | 65 ++ .../java/statemachine/StateMachineTest.java | 76 ++ .../java/statemachine/StateMachineTester.java | 12 + ...ryptographicArchitecture-3.0.1-ruleset.zip | Bin 0 -> 22208 bytes .../resources/parser/Multiple-rulesets.zip | Bin 0 -> 76225 bytes .../src/test/resources/parser/empty.zip | Bin 0 -> 22 bytes .../test/resources/parser/rulesetWithJunk.zip | Bin 0 -> 27641 bytes .../StateMachineTester.crysl | 11 + .../StateMachineTester.crysl | 11 + de.darmstadt.tu.crossing.CrySL/pom.xml | 1 - pom.xml | 20 +- 42 files changed, 3234 insertions(+), 5 deletions(-) create mode 100644 CryslParser/pom.xml create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/CrySLParser.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReader.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReaderClassPath.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLReaderUtils.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CryslException.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/ExceptionsReader.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/StateMachineGraphBuilder.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLArithmeticConstraint.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLComparisonConstraint.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLCondPredicate.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLConstraint.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLException.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLExceptionConstraint.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLForbiddenMethod.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLLiteral.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLMethod.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLObject.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLPredicate.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLRule.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLSplitter.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLValueConstraint.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/FiniteStateMachine.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ICrySLPredicateParameter.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ISLConstraint.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraph.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraphReader.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateNode.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/Transition.java create mode 100644 CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/TransitionEdge.java create mode 100644 CryslParser/src/main/resources/plugin.properties create mode 100644 CryslParser/src/test/java/parser/CrySLParserTest.java create mode 100644 CryslParser/src/test/java/statemachine/StateMachineTest.java create mode 100644 CryslParser/src/test/java/statemachine/StateMachineTester.java create mode 100644 CryslParser/src/test/resources/parser/JavaCryptographicArchitecture-3.0.1-ruleset.zip create mode 100644 CryslParser/src/test/resources/parser/Multiple-rulesets.zip create mode 100644 CryslParser/src/test/resources/parser/empty.zip create mode 100644 CryslParser/src/test/resources/parser/rulesetWithJunk.zip create mode 100644 CryslParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl create mode 100644 CryslParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl diff --git a/CryslParser/pom.xml b/CryslParser/pom.xml new file mode 100644 index 00000000..814779a8 --- /dev/null +++ b/CryslParser/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + ../pom.xml + + + CrySLParser + + CrySL-Parser + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + + + + + ${project.groupId} + de.darmstadt.tu.crossing.CrySL + ${project.version} + + + org.eclipse.xtext + org.eclipse.xtext + ${xtextVersion} + + + org.eclipse.xtext + org.eclipse.xtext.xbase + ${xtextVersion} + + + org.eclipse.xtext + org.eclipse.xtext.common.types + ${xtextVersion} + + + org.eclipse.emf + org.eclipse.emf.common + ${emfVersion} + + + org.eclipse.emf + org.eclipse.emf.ecore + ${emfVersion} + + + javax.servlet + javax.servlet-api + 4.0.1 + + + org.slf4j + slf4j-api + 2.0.16 + + + org.slf4j + slf4j-simple + 2.0.16 + + + junit + junit + 4.13.2 + test + + + diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/CrySLParser.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/CrySLParser.java new file mode 100644 index 00000000..e7cf8f36 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/CrySLParser.java @@ -0,0 +1,192 @@ +package de.darmstadt.tu.crossing; + +import de.darmstadt.tu.crossing.parsing.CrySLException; +import de.darmstadt.tu.crossing.parsing.CrySLModelReader; +import de.darmstadt.tu.crossing.rule.CrySLRule; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CrySLParser { + + private static final Logger LOGGER = LoggerFactory.getLogger(CrySLParser.class); + private static final String CRYSL_FILE_ENDING = ".crysl"; + + /** + * Reads the rules from a specific path. The path can either direct to a directory containing + * the .crysl files or a .zip file. + * + * @param path path to a directory or .zip file containing the .crysl files + * @return the {@link CrySLRule} objects from the path + * @throws IOException if there are problems reading the files + */ + public Collection parseRulesFromPath(String path) throws IOException { + if (isZipFile(path)) { + return parseRulesFromZipArchive(path); + } + + return parseRulesFromDirectory(path); + } + + public Collection parseRulesFromDirectory(String path) throws IOException { + File directory = new File(path); + if (!directory.exists()) { + throw new FileNotFoundException("Directory " + path + " does not exist"); + } + + if (!directory.isDirectory()) { + throw new IOException(path + " is not a directory"); + } + + Collection files = Arrays.asList(directory.listFiles()); + return parseRulesFromFiles(files); + } + + public Collection parseRulesFromFiles(Collection files) { + Collection result = new HashSet<>(); + + for (File file : files) { + try { + CrySLRule rule = parseRuleFromFile(file); + + if (result.contains(rule)) { + LOGGER.warn("Rule for class {} appears multiple times", rule.getClassName()); + continue; + } + + result.add(rule); + } catch (CrySLException e) { + LOGGER.error(e.getMessage()); + } + } + return result; + } + + public CrySLRule parseRuleFromFile(File file) throws CrySLException { + String fileName = file.getName(); + if (!fileName.endsWith(CRYSL_FILE_ENDING)) { + throw new CrySLException( + "The extension of " + fileName + " does not match " + CRYSL_FILE_ENDING); + } + + CrySLModelReader modelReader = new CrySLModelReader(); + return modelReader.readRule(file); + } + + private boolean isZipFile(String path) { + File file = new File(path); + + // Copied from + // https://stackoverflow.com/questions/33934178/how-to-identify-a-zip-file-in-java + int fileSignature; + + try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { + fileSignature = raf.readInt(); + } catch (IOException e) { + return false; + } + return fileSignature == 0x504B0304 + || fileSignature == 0x504B0506 + || fileSignature == 0x504B0708; + } + + public Collection parseRulesFromZipArchive(String path) throws IOException { + Collection result = new HashSet<>(); + File file = new File(path); + + try (ZipFile zipFile = new ZipFile(file)) { + Enumeration entries = zipFile.entries(); + + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + + if (entry.isDirectory()) { + continue; + } + + try { + CrySLRule rule = parseRuleFromZipEntry(entry, zipFile, file); + result.add(rule); + } catch (CrySLException e) { + LOGGER.error(e.getMessage()); + } + } + } + return result; + } + + private CrySLRule parseRuleFromZipEntry(ZipEntry entry, ZipFile zipFile, File file) + throws CrySLException { + String entryName = entry.getName(); + if (entry.isDirectory() || !entryName.endsWith(CRYSL_FILE_ENDING)) { + throw new CrySLException("ZIP entry " + entryName + " is not a CrySL file"); + } + + try { + String name = createUniqueZipEntryName(file, entry); + CrySLModelReader reader = new CrySLModelReader(); + + InputStream inputStream = zipFile.getInputStream(entry); + CrySLRule rule = reader.readRule(inputStream, name); + inputStream.close(); + + return rule; + } catch (IOException ex) { + throw new CrySLException( + "Could not read file " + + entry.getName() + + " from Zip archive " + + ex.getMessage()); + } + } + + /** + * For zip file entries there is no real URI. Using the raw absolute path of the zip file will + * cause an exception when trying to resolve/create the resource in the {@link + * CrySLModelReader#readRule(File)} methods. Solution: Create a custom URI with the following + * scheme: uri := [HexHashedAbsoluteZipFilePath][SystemFileSeparator][ZipEntryName] This scheme + * has the properties that it still is unique system-wide, The hash will be the same for the + * same file, so you could know if two rules come from the same ruleset file, and you still can + * get the information of the zipped file. + * + * @param zipFile the File that holds the zip archive + * @param zipEntry the Zip entry to create the name for + * @return the unique name + */ + private static String createUniqueZipEntryName(File zipFile, ZipEntry zipEntry) { + StringBuilder sb = new StringBuilder(); + + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + messageDigest.update(zipFile.getAbsolutePath().getBytes()); + byte[] updatedFileName = messageDigest.digest(zipFile.getAbsolutePath().getBytes()); + + String partFileName = bytesToHex(updatedFileName); + sb.append(partFileName); + sb.append(File.separator); + sb.append(zipEntry.getName()); + return sb.toString(); + } + + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) sb.append(String.format("%02x", b)); + return sb.toString(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReader.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReader.java new file mode 100644 index 00000000..6472d4ca --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReader.java @@ -0,0 +1,650 @@ +package de.darmstadt.tu.crossing.parsing; + +import com.google.inject.Injector; +import de.darmstadt.tu.crossing.CrySLStandaloneSetup; +import de.darmstadt.tu.crossing.crySL.AlternativeRequiredPredicates; +import de.darmstadt.tu.crossing.crySL.BuiltinPredicate; +import de.darmstadt.tu.crossing.crySL.ConditionalPredicate; +import de.darmstadt.tu.crossing.crySL.Constraint; +import de.darmstadt.tu.crossing.crySL.ConstraintsBlock; +import de.darmstadt.tu.crossing.crySL.Domainmodel; +import de.darmstadt.tu.crossing.crySL.EnsuresBlock; +import de.darmstadt.tu.crossing.crySL.Event; +import de.darmstadt.tu.crossing.crySL.EventsBlock; +import de.darmstadt.tu.crossing.crySL.ForbiddenBlock; +import de.darmstadt.tu.crossing.crySL.ForbiddenMethod; +import de.darmstadt.tu.crossing.crySL.LabeledMethodCall; +import de.darmstadt.tu.crossing.crySL.Literal; +import de.darmstadt.tu.crossing.crySL.LiteralExpression; +import de.darmstadt.tu.crossing.crySL.LiteralList; +import de.darmstadt.tu.crossing.crySL.NegatesBlock; +import de.darmstadt.tu.crossing.crySL.ObjectExpression; +import de.darmstadt.tu.crossing.crySL.ObjectOperation; +import de.darmstadt.tu.crossing.crySL.ObjectReference; +import de.darmstadt.tu.crossing.crySL.ObjectsBlock; +import de.darmstadt.tu.crossing.crySL.Operator; +import de.darmstadt.tu.crossing.crySL.Order; +import de.darmstadt.tu.crossing.crySL.OrderBlock; +import de.darmstadt.tu.crossing.crySL.Predicate; +import de.darmstadt.tu.crossing.crySL.PredicateParameter; +import de.darmstadt.tu.crossing.crySL.RequiredPredicate; +import de.darmstadt.tu.crossing.crySL.RequiresBlock; +import de.darmstadt.tu.crossing.crySL.ThisPredicateParameter; +import de.darmstadt.tu.crossing.crySL.TimedPredicate; +import de.darmstadt.tu.crossing.crySL.WildcardPredicateParameter; +import de.darmstadt.tu.crossing.rule.CrySLArithmeticConstraint; +import de.darmstadt.tu.crossing.rule.CrySLComparisonConstraint; +import de.darmstadt.tu.crossing.rule.CrySLCondPredicate; +import de.darmstadt.tu.crossing.rule.CrySLConstraint; +import de.darmstadt.tu.crossing.rule.CrySLForbiddenMethod; +import de.darmstadt.tu.crossing.rule.CrySLMethod; +import de.darmstadt.tu.crossing.rule.CrySLObject; +import de.darmstadt.tu.crossing.rule.CrySLPredicate; +import de.darmstadt.tu.crossing.rule.CrySLRule; +import de.darmstadt.tu.crossing.rule.CrySLSplitter; +import de.darmstadt.tu.crossing.rule.CrySLValueConstraint; +import de.darmstadt.tu.crossing.rule.ICrySLPredicateParameter; +import de.darmstadt.tu.crossing.rule.ISLConstraint; +import de.darmstadt.tu.crossing.rule.StateMachineGraph; +import de.darmstadt.tu.crossing.rule.StateNode; +import de.darmstadt.tu.crossing.rule.TransitionEdge; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.xtext.common.types.JvmDeclaredType; +import org.eclipse.xtext.common.types.JvmTypeReference; +import org.eclipse.xtext.common.types.access.impl.ClasspathTypeProvider; +import org.eclipse.xtext.diagnostics.Severity; +import org.eclipse.xtext.resource.XtextResourceSet; +import org.eclipse.xtext.util.CancelIndicator; +import org.eclipse.xtext.validation.CheckMode; +import org.eclipse.xtext.validation.IResourceValidator; +import org.eclipse.xtext.validation.Issue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CrySLModelReader { + + private static final Logger LOGGER = LoggerFactory.getLogger(CrySLModelReader.class); + + private StateMachineGraph smg = null; + private JvmTypeReference currentClass; + private final XtextResourceSet resourceSet; + private final Injector injector; + public static final String cryslFileEnding = ".crysl"; + + private static final String THIS = "this"; + private static final String NULL = "null"; + private static final String UNDERSCORE = "_"; + + /** Creates a CrySLModelReader which creates rules from classes on the run time's classpath. */ + public CrySLModelReader() { + this(CrySLModelReaderClassPath.JAVA_CLASS_PATH); + } + + /** + * Creates a CrySLModelReader which creates rules from classes on the run time's classpath and a + * given virtual classpath. + * + * @param classPath Contains additional classpath elements which are not present on the current + * run time's classpath. + */ + public CrySLModelReader(CrySLModelReaderClassPath classPath) { + CrySLStandaloneSetup crySLStandaloneSetup = new CrySLStandaloneSetup(); + this.injector = crySLStandaloneSetup.createInjectorAndDoEMFRegistration(); + this.resourceSet = injector.getInstance(XtextResourceSet.class); + URL[] classpath = classPath.getClassPath(); + URLClassLoader ucl = new URLClassLoader(classpath); + this.resourceSet.setClasspathURIContext(new URLClassLoader(classpath)); + new ClasspathTypeProvider(ucl, this.resourceSet, null, null); + } + + /** + * Reads the content of a CrySL file from an {@link InputStream}, afterward the {@link + * CrySLRule} will be created. + * + * @param stream the {@link InputStream} holds the CrySL file content + * @param virtualFileName the name needs following structure + * [HexHashedAbsoluteZipFilePath][SystemFileSeparator][ZipEntryName] + * @return the {@link CrySLRule} + * @throws IllegalArgumentException If the file for the rule cannot be found + * @throws IOException If there is a problem with reading the file + * @throws CrySLException If the file is not a .crysl file + */ + public CrySLRule readRule(InputStream stream, String virtualFileName) + throws IOException, CrySLException { + if (!virtualFileName.endsWith(cryslFileEnding)) { + throw new CrySLException( + "The extension of " + virtualFileName + " does not match " + cryslFileEnding); + } + + URI uri = URI.createURI(virtualFileName); + Resource resource = resourceSet.getURIResourceMap().get(uri); + + if (resource == null) { + resource = resourceSet.createResource(uri); + resource.load(stream, Collections.EMPTY_MAP); + } + + return createRuleFromResource(resource); + } + + /** + * Reads the content of a CrySL file and returns a {@link CrySLRule} object. + * + * @param ruleFile the CrySL file + * @return the {@link CrySLRule} object + * @throws CrySLException If the file is not a .crysl file + */ + public CrySLRule readRule(File ruleFile) throws CrySLException { + Resource resource = + resourceSet.getResource(URI.createFileURI(ruleFile.getAbsolutePath()), true); + + return createRuleFromResource(resource); + } + + private boolean runValidator(Resource resource) { + Severity report = Severity.WARNING; + IResourceValidator validator = injector.getInstance(IResourceValidator.class); + Collection issues = + validator.validate(resource, CheckMode.ALL, CancelIndicator.NullImpl); + boolean errorFound = false; + + for (Issue issue : issues) { + switch (issue.getSeverity()) { + case ERROR: + if (report.compareTo(issue.getSeverity()) >= 0) + LOGGER.error( + "{}:{}: {}", + resource.getURI(), + issue.getLineNumber(), + issue.getMessage()); + errorFound = true; + break; + case WARNING: + if (report.compareTo(issue.getSeverity()) >= 0) + LOGGER.warn( + "{}:{}: {}", + resource.getURI(), + issue.getLineNumber(), + issue.getMessage()); + errorFound = true; + break; + case INFO: + if (report.compareTo(issue.getSeverity()) >= 0) + LOGGER.info( + "{}:{}: {}", + resource.getURI(), + issue.getLineNumber(), + issue.getMessage()); + break; + case IGNORE: + break; + } + } + return errorFound; + } + + private CrySLRule createRuleFromResource(Resource resource) throws CrySLException { + if (resource == null) { + throw new CrySLException( + "Internal error creating a CrySL rule: 'resource parameter was null'."); + } + + if (runValidator(resource)) { + throw new CrySLException( + "Skipping rule since it contains errors: " + resource.getURI()); + } + + try { + return createRuleFromDomainModel((Domainmodel) resource.getContents().get(0)); + } catch (Exception e) { + throw new CrySLException( + "An error occurred while reading the rule " + resource.getURI()); + } + } + + private CrySLRule createRuleFromDomainModel(Domainmodel model) throws CrySLException { + this.currentClass = model.getJavaType(); + String currentClass = this.currentClass.getQualifiedName(); + + if (currentClass.equals("void")) { + throw new CrySLException("Class for the rule is not on the classpath."); + } + + final Collection> objects = getObjects(model.getObjects()); + + Collection forbiddenMethods = + getForbiddenMethods(model.getForbidden()); + + final EventsBlock eventsBlock = model.getEvents(); + final OrderBlock orderBlock = model.getOrder(); + final Collection events = changeDeclaringClass(this.currentClass, eventsBlock); + final Order order = orderBlock == null ? null : orderBlock.getOrder(); + this.smg = StateMachineGraphBuilder.buildSMG(order, events); + + final Collection constraints = new ArrayList<>(); + constraints.addAll(getConstraints(model.getConstraints())); + constraints.addAll(getRequiredPredicates(model.getRequires())); + + // Since 3.0.0: All sections are optional + final Collection eventMethods = new HashSet<>(); + if (eventsBlock != null) { + eventMethods.addAll(CrySLReaderUtils.resolveEventsToCryslMethods(events)); + constraints.addAll(ExceptionsReader.getExceptionConstraints(eventsBlock)); + } + + final EnsuresBlock ensuresBlock = model.getEnsures(); + final NegatesBlock negatesBlock = model.getNegates(); + + final Collection predicates = + new ArrayList<>(getEnsuredPredicates(ensuresBlock)); + final Collection negatedPredicates = + new ArrayList<>(getNegatedPredicates(negatesBlock)); + + return new CrySLRule( + currentClass, + objects, + forbiddenMethods, + eventMethods, + this.smg, + constraints, + predicates, + negatedPredicates); + } + + private Collection changeDeclaringClass( + JvmTypeReference currentClass, EventsBlock eventsBlock) { + if (eventsBlock == null) { + return Collections.emptyList(); + } + + return eventsBlock.getEvents().stream() + .map( + event -> + event instanceof LabeledMethodCall + ? changeDeclaringClass( + currentClass, (LabeledMethodCall) event) + : event) + .collect(Collectors.toList()); + } + + private Event changeDeclaringClass(JvmTypeReference currentClass, LabeledMethodCall event) { + event.getMethod().getMethod().setDeclaringType((JvmDeclaredType) currentClass.getType()); + return event; + } + + private Collection> getObjects(final ObjectsBlock objects) { + if (objects == null) { + return Collections.emptyList(); + } + return objects.getDeclarations().parallelStream() + .map(CrySLReaderUtils::resolveObject) + .collect(Collectors.toList()); + } + + private Collection getForbiddenMethods(final ForbiddenBlock forbidden) { + if (forbidden == null) { + return Collections.emptyList(); + } + + Collection forbiddenMethods = new ArrayList<>(); + + for (final ForbiddenMethod method : forbidden.getForbiddenMethods()) { + CrySLMethod cryslMethod = CrySLReaderUtils.toCrySLMethod(method); + Collection alternatives = + CrySLReaderUtils.resolveEventToCryslMethods(method.getReplacement()); + forbiddenMethods.add(new CrySLForbiddenMethod(cryslMethod, alternatives)); + } + return forbiddenMethods; + } + + private Collection getEnsuredPredicates(final EnsuresBlock ensures) { + if (ensures == null) { + return Collections.emptyList(); + } + + return getTimedPredicates(ensures.getEnsuredPredicates(), false); + } + + private Collection getNegatedPredicates(final NegatesBlock negates) { + if (negates == null) { + return Collections.emptyList(); + } + return getTimedPredicates(negates.getNegatedPredicates(), true); + } + + private Collection getTimedPredicates( + final Collection timedPredicates, boolean negate) { + Collection predicates = new ArrayList<>(timedPredicates.size()); + + for (final TimedPredicate timed : timedPredicates) { + Predicate predicate = timed.getPredicate(); + ISLConstraint constraint = getPredicateCondition(timed); + List parameters = resolvePredicateParameters(predicate); + String name = predicate.getName(); + + if (timed.getAfter() == null) { + predicates.add(new CrySLPredicate(null, name, parameters, negate, constraint)); + } else { + Collection nodes = + getStatesForMethods( + CrySLReaderUtils.resolveEventToCryslMethods(timed.getAfter())); + predicates.add( + new CrySLCondPredicate(null, name, parameters, negate, nodes, constraint)); + } + } + return predicates; + } + + private List resolvePredicateParameters(Predicate predicate) { + final List parameters = + new ArrayList<>(predicate.getParameters().size()); + + for (PredicateParameter parameter : predicate.getParameters()) { + if (parameter instanceof WildcardPredicateParameter) { + parameters.add(new CrySLObject(UNDERSCORE, NULL)); + } else if (parameter instanceof ThisPredicateParameter) { + parameters.add(new CrySLObject(THIS, this.currentClass.getQualifiedName())); + } else { + parameters.add(getObjectExpressionValue(parameter.getValue())); + } + } + return parameters; + } + + private CrySLObject getObjectExpressionValue(ObjectExpression expression) { + if (expression instanceof ObjectReference) { + return getObjectExpressionValue((ObjectReference) expression); + } + if (expression instanceof ObjectOperation) { + return getObjectExpressionValue((ObjectOperation) expression); + } + return null; + } + + private CrySLObject getObjectExpressionValue(ObjectReference reference) { + return CrySLReaderUtils.toCrySLObject(reference.getObject()); + } + + private CrySLObject getObjectExpressionValue(ObjectOperation operation) { + String type = operation.getObject().getType().getQualifiedName(); + String name = operation.getObject().getName(); + switch (operation.getFn()) { + case ALG: + return new CrySLObject(name, type, new CrySLSplitter(0, "/")); + case MODE: + return new CrySLObject(name, type, new CrySLSplitter(1, "/")); + case PAD: + return new CrySLObject(name, type, new CrySLSplitter(2, "/")); + case PART: + int index = Integer.parseInt(operation.getIndex()); + String split = operation.getSplit(); + return new CrySLObject(name, type, new CrySLSplitter(index, split)); + case ELEMENTS: // It does basically nothing + return CrySLReaderUtils.toCrySLObject(operation.getObject()); + default: + return null; + } + } + + private ISLConstraint getPredicateCondition(ConditionalPredicate predicate) { + EObject condition = predicate.getCondition(); + if (condition instanceof Constraint) { + return getConstraint((Constraint) condition); + } + if (condition instanceof Predicate) { + return getPredicate((Predicate) condition); + } + return null; + } + + private CrySLPredicate getPredicate(Predicate predicate) { + return getPredicate(predicate, false, null); + } + + private CrySLPredicate getPredicate( + Predicate predicate, boolean negate, ISLConstraint constraint) { + final List variables = resolvePredicateParameters(predicate); + return new CrySLPredicate(null, predicate.getName(), variables, negate, constraint); + } + + private Collection getRequiredPredicates(RequiresBlock requiresBlock) { + if (requiresBlock == null) { + return Collections.emptyList(); + } + + final Collection predicates = new ArrayList<>(); + final Collection requiredPredicates = + requiresBlock.getRequiredPredicates(); + + for (AlternativeRequiredPredicates alternativePredicates : requiredPredicates) { + List alternatives = + alternativePredicates.getAlternatives().parallelStream() + .map(this::getRequiredPredicate) + .collect(Collectors.toList()); + ISLConstraint predicate = alternatives.get(0); + + for (int i = 1; i < alternatives.size(); i++) + predicate = + new CrySLConstraint( + alternatives.get(i), predicate, CrySLConstraint.LogOps.or); + predicates.add(predicate); + } + return predicates; + } + + private CrySLPredicate getRequiredPredicate(RequiredPredicate predicate) { + ISLConstraint constraint = getPredicateCondition(predicate); + boolean negate = predicate.isNegated(); + return getPredicate(predicate.getPredicate(), negate, constraint); + } + + private Collection getConstraints(ConstraintsBlock constraintsBlock) { + if (constraintsBlock == null) { + return Collections.emptyList(); + } + return constraintsBlock.getConstraints().parallelStream() + .map(this::getConstraint) + .collect(Collectors.toList()); + } + + private ISLConstraint getConstraint(final Constraint constraint) { + + if (constraint instanceof LiteralExpression) { + return getLiteralExpression((LiteralExpression) constraint); + } + + switch (constraint.getOp()) { + /* Logical Expressions */ + case NOT: + // NOT operator was only implemented for Predicates, which were + // not reachable from the Constraint rule. + // Add it to LogOps? + throw new UnsupportedOperationException("The NOT operator is not implemented."); + case IMPLY: + case OR: + case AND: + { + ISLConstraint left = getConstraint(constraint.getLeft()); + ISLConstraint right = getConstraint(constraint.getRight()); + CrySLConstraint.LogOps logOp = + CrySLReaderUtils.logOpFromOperator(constraint.getOp()).get(); + return new CrySLConstraint(left, right, logOp); + } + /* Comparison Expressions */ + case EQUAL: // LogOps specifies eq as well, but it was not used + case UNEQUAL: + case GREATER: + case GREATER_OR_EQUAL: + case LESS: + case LESS_OR_EQUAL: + { + CrySLComparisonConstraint.CompOp compOp = + CrySLReaderUtils.compOpFromOperator(constraint.getOp()).get(); + CrySLArithmeticConstraint left = + coerceConstraintToArithmeticConstraint( + getConstraint(constraint.getLeft())); + CrySLArithmeticConstraint right = + coerceConstraintToArithmeticConstraint( + getConstraint(constraint.getRight())); + return new CrySLComparisonConstraint(left, right, compOp); + } + /* Arithmetic Expressions */ + case TIMES: + case DIVIDE: + // These were specified in Syntax, but not implemented here. + // Add it to ArithOp? + throw new UnsupportedOperationException( + "The multiplication operators are not implemented."); + case PLUS: + case MINUS: + case MODULO: + { + ISLConstraint left = getConstraint(constraint.getLeft()); + ISLConstraint right = getConstraint(constraint.getRight()); + CrySLArithmeticConstraint.ArithOp arithOp = + CrySLReaderUtils.arithOpFromOperator(constraint.getOp()).get(); + return new CrySLArithmeticConstraint(left, right, arithOp); + } + /* In Expression */ + case IN: + { + CrySLObject left = + constraint.getLeft() instanceof ObjectExpression + ? getObjectExpressionValue( + (ObjectExpression) constraint.getLeft()) + : constraint.getLeft() instanceof Literal + ? CrySLReaderUtils.toCrySLObject( + (Literal) constraint.getLeft()) + : null; + if (left == null) + throw new IllegalArgumentException( + "lhs of an IN expression must be an Object or an Operation thereon."); + LiteralList right = (LiteralList) constraint.getRight(); + List values = + right.getElements().stream() + .map(Literal::getValue) + .collect(Collectors.toList()); + return new CrySLValueConstraint(left, values); + } + } + return null; + } + + private CrySLArithmeticConstraint coerceConstraintToArithmeticConstraint( + ISLConstraint constraint) { + if (constraint instanceof CrySLArithmeticConstraint) { + return (CrySLArithmeticConstraint) constraint; + } + if (constraint instanceof CrySLPredicate) { + return makeArithmeticConstraint(constraint); + } + throw new ClassCastException( + "Cant coerce `" + constraint.toString() + "` into ArithmeticExpression"); + } + + private ISLConstraint getLiteralExpression(LiteralExpression expression) { + if (expression instanceof BuiltinPredicate) { + return getBuiltinPredicate((BuiltinPredicate) expression); + } + if (expression instanceof Literal) { + return makeConstraintFromObject(CrySLReaderUtils.toCrySLObject((Literal) expression)); + } + if (expression instanceof ObjectExpression) { + return makeConstraintFromObject( + getObjectExpressionValue((ObjectExpression) expression)); + } + return null; + } + + /** This is weird, but is taken from the original implementation. */ + private ISLConstraint makeConstraintFromObject(ICrySLPredicateParameter object) { + return makeArithmeticConstraint(object); + } + + private CrySLArithmeticConstraint makeArithmeticConstraint(ICrySLPredicateParameter object) { + CrySLObject zero = new CrySLObject("0", "int"); + CrySLArithmeticConstraint.ArithOp plus = + CrySLReaderUtils.arithOpFromOperator(Operator.PLUS).get(); + return new CrySLArithmeticConstraint(object, zero, plus); + } + + private Collection getStatesForMethods(final Collection condition) { + final Collection predicateGenerationNodes = new HashSet<>(); + if (condition.isEmpty()) { + return predicateGenerationNodes; + } + + for (final TransitionEdge transition : this.smg.getAllTransitions()) { + if (transition.getLabel().containsAll(condition)) { + Collection reachableNodes = + getAllReachableNodes(transition.getRight(), new HashSet<>()); + + predicateGenerationNodes.addAll(reachableNodes); + } + } + return predicateGenerationNodes; + } + + private Collection getAllReachableNodes( + final StateNode startNode, Collection visited) { + if (visited.contains(startNode)) { + return visited; + } + + visited.add(startNode); + + for (TransitionEdge edge : this.smg.getAllOutgoingEdges(startNode)) { + Collection reachableNodes = getAllReachableNodes(edge.getRight(), visited); + + visited.addAll(reachableNodes); + } + return visited; + } + + private ISLConstraint getBuiltinPredicate(BuiltinPredicate builtinPredicate) { + String name = builtinPredicate.getPredicate().getLiteral(); + List parameters; + boolean negated = false; + + switch (builtinPredicate.getPredicate()) { + case NO_CALL_TO: + case CALL_TO: + parameters = + CrySLReaderUtils.resolveEventToPredicateParameters( + builtinPredicate.getEvent()); + break; + + case INSTANCE_OF: + case NEVER_TYPE_OF: + parameters = + List.of( + CrySLReaderUtils.toCrySLObject(builtinPredicate.getObject()), + new CrySLObject( + NULL, builtinPredicate.getType().getQualifiedName())); + break; + + case NOT_HARD_CODED: + case LENGTH: + CrySLObject object = CrySLReaderUtils.toCrySLObject(builtinPredicate.getObject()); + parameters = Collections.singletonList(object); + break; + default: + parameters = Collections.emptyList(); + } + return new CrySLPredicate(null, name, parameters, negated); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReaderClassPath.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReaderClassPath.java new file mode 100644 index 00000000..c3d0e22b --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReaderClassPath.java @@ -0,0 +1,135 @@ +package de.darmstadt.tu.crossing.parsing; + +import com.google.common.base.Splitter; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Appendable, virtual classpath extension, allowing to add custom elements, even after the actual + * classpath was already set. + */ +public class CrySLModelReaderClassPath { + + private static final Logger LOGGER = LoggerFactory.getLogger(CrySLModelReaderClassPath.class); + + private static final URL[] javaRuntimeClassPath; + private final Collection virtualClassPath; + + public static final CrySLModelReaderClassPath JAVA_CLASS_PATH = new CrySLModelReaderClassPath(); + + static { + Collection runtimeClassPath = + Splitter.on(File.pathSeparatorChar) + .splitToList(System.getProperty("java.class.path")); + javaRuntimeClassPath = + runtimeClassPath.stream() + .map( + (it) -> { + try { + return new File(it).toURI().toURL(); + } catch (MalformedURLException e) { + LOGGER.warn( + "Unable to get URL from java classpath element '" + + it + + "'.", + e); + return null; + } + }) + .filter(Objects::nonNull) + .toArray(URL[]::new); + } + + /** Initializes a new instance with the current run time's classpath. */ + private CrySLModelReaderClassPath() { + virtualClassPath = Collections.emptySet(); + } + + /** + * Initializes a new instance with the current run time's classpath and the elements the given + * set. + * + * @param virtualClassPath the virtual class path + */ + public CrySLModelReaderClassPath(Collection virtualClassPath) { + this.virtualClassPath = virtualClassPath; + } + + /** + * Creates a new instance with the current run time's classpath and the elements the given set. + * + * @param virtualClassPath the virtual class path + * @return the model reader class path instance for the given virtual class path + */ + public static CrySLModelReaderClassPath createFromPaths(Collection virtualClassPath) { + Collection urlClassPath = + virtualClassPath.stream() + .map( + (it) -> { + try { + return it.toAbsolutePath().toUri().toURL(); + } catch (MalformedURLException e) { + LOGGER.warn( + "Unable to get URL from virtual classpath element '" + + it.toAbsolutePath() + + "'.", + e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + return new CrySLModelReaderClassPath(urlClassPath); + } + + /** + * Creates a new instance with the current run time's classpath and the elements the given set. + * + * @param virtualClassPath the virtual class path + * @return the model reader class path instance for the given virtual class path + */ + public static CrySLModelReaderClassPath createFromURIs(Collection virtualClassPath) { + Collection urlClassPath = + virtualClassPath.stream() + .map( + (it) -> { + try { + return it.toURL(); + } catch (MalformedURLException e) { + LOGGER.warn( + "Unable to get URL from virtual classpath element '" + + it + + "'.", + e); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + return new CrySLModelReaderClassPath(urlClassPath); + } + + /** + * Get the class path. + * + * @return A copy of the current state of the classpath. + */ + public URL[] getClassPath() { + if (virtualClassPath.isEmpty()) return javaRuntimeClassPath.clone(); + Collection classPath = new HashSet<>(); + classPath.addAll(Arrays.asList(javaRuntimeClassPath)); + classPath.addAll(virtualClassPath); + return classPath.toArray(new URL[0]); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLReaderUtils.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLReaderUtils.java new file mode 100644 index 00000000..9673c3ae --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLReaderUtils.java @@ -0,0 +1,217 @@ +package de.darmstadt.tu.crossing.parsing; + +import de.darmstadt.tu.crossing.crySL.Aggregate; +import de.darmstadt.tu.crossing.crySL.AnyParameterType; +import de.darmstadt.tu.crossing.crySL.BooleanLiteral; +import de.darmstadt.tu.crossing.crySL.Constraint; +import de.darmstadt.tu.crossing.crySL.Event; +import de.darmstadt.tu.crossing.crySL.Exception; +import de.darmstadt.tu.crossing.crySL.ExceptionAggregate; +import de.darmstadt.tu.crossing.crySL.ExceptionDeclaration; +import de.darmstadt.tu.crossing.crySL.ForbiddenMethod; +import de.darmstadt.tu.crossing.crySL.IntLiteral; +import de.darmstadt.tu.crossing.crySL.LabeledMethodCall; +import de.darmstadt.tu.crossing.crySL.Literal; +import de.darmstadt.tu.crossing.crySL.Method; +import de.darmstadt.tu.crossing.crySL.Object; +import de.darmstadt.tu.crossing.crySL.Operator; +import de.darmstadt.tu.crossing.crySL.StringLiteral; +import de.darmstadt.tu.crossing.rule.CrySLArithmeticConstraint; +import de.darmstadt.tu.crossing.rule.CrySLComparisonConstraint; +import de.darmstadt.tu.crossing.rule.CrySLConstraint; +import de.darmstadt.tu.crossing.rule.CrySLException; +import de.darmstadt.tu.crossing.rule.CrySLMethod; +import de.darmstadt.tu.crossing.rule.CrySLObject; +import de.darmstadt.tu.crossing.rule.ICrySLPredicateParameter; +import java.util.AbstractMap.SimpleEntry; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.eclipse.xtext.common.types.JvmTypeParameter; + +public class CrySLReaderUtils { + + public static Collection resolveEventToCryslMethods(final Event event) { + return resolveEventToCryslMethodsStream(event).collect(Collectors.toList()); + } + + public static List resolveEventToPredicateParameters( + final Event event) { + return resolveEventToCryslMethodsStream(event).collect(Collectors.toList()); + } + + public static Collection resolveEventsToCryslMethods( + final Collection events) { + return resolveEventsToCryslMethodsStream(events).collect(Collectors.toList()); + } + + public static Stream resolveEventsToCryslMethodsStream( + final Collection events) { + return events.parallelStream().flatMap(CrySLReaderUtils::resolveEventToCryslMethodsStream); + } + + public static Stream resolveEventToCryslMethodsStream(final Event event) { + if (event instanceof Aggregate) return resolveEventToCryslMethodsStream((Aggregate) event); + if (event instanceof LabeledMethodCall) + return resolveEventToCryslMethodsStream((LabeledMethodCall) event); + return Stream.empty(); + } + + protected static Stream resolveEventToCryslMethodsStream( + final Aggregate aggregate) { + return aggregate.getEvents().parallelStream() + .flatMap(CrySLReaderUtils::resolveEventToCryslMethodsStream); + } + + protected static Stream resolveEventToCryslMethodsStream( + final LabeledMethodCall event) { + return Stream.of(toCrySLMethod((event.getMethod()))); + } + + public static CrySLMethod toCrySLMethod(final ForbiddenMethod method) { + String name = method.getMethod().getQualifiedName(); + String declaringClassType = method.getMethod().getDeclaringType().getQualifiedName(); + List> parameters = + method.getParameters().stream() + .map( + parameter -> + new SimpleEntry<>( + parameter.getSimpleName(), + parameter.getType().getQualifiedName())) + .collect(Collectors.toList()); + return new CrySLMethod(name, declaringClassType, parameters, resolveObject(null)); + } + + public static CrySLMethod toCrySLMethod(final Method method) { + String name = method.getMethod().getQualifiedName(); + String declaringClassType = method.getMethod().getDeclaringType().getQualifiedName(); + List> parameters = + method.getParameters().stream() + .map( + parameter -> + parameter instanceof AnyParameterType + ? new SimpleEntry<>( + CrySLMethod.NO_NAME, CrySLMethod.ANY_TYPE) + : resolveObject((parameter.getValue()))) + .collect(Collectors.toList()); + return new CrySLMethod( + name, declaringClassType, parameters, resolveObject(method.getReturn())); + } + + public static CrySLObject toCrySLObject(Object object) { + return new CrySLObject(object.getName(), object.getType().getQualifiedName()); + } + + public static CrySLObject toCrySLObject(Literal literal) { + String value = literal.getValue(); + String type = + literal instanceof IntLiteral + ? "int" + : literal instanceof BooleanLiteral + ? "boolean" + : literal instanceof StringLiteral + ? String.class.getName() + : "void"; + return new CrySLObject(value, type); + } + + public static Stream resolveExceptionsStream(final Exception exception) { + if (exception instanceof ExceptionDeclaration) + return resolveExceptionsStream((ExceptionDeclaration) exception); + if (exception instanceof ExceptionAggregate) + return resolveExceptionsStream((ExceptionAggregate) exception); + return Stream.empty(); + } + + protected static Stream resolveExceptionsStream( + final ExceptionAggregate exception) { + return exception.getExceptions().stream() + .flatMap(CrySLReaderUtils::resolveExceptionsStream); + } + + protected static Stream resolveExceptionsStream( + final ExceptionDeclaration exception) { + return Stream.of(toCrySLException(exception)); + } + + public static CrySLException toCrySLException(final ExceptionDeclaration exception) { + return new CrySLException(exception.getException().getIdentifier()); + } + + public static Map.Entry resolveObject(final Object o) { + if (o == null) { + return new SimpleEntry<>(CrySLMethod.NO_NAME, CrySLMethod.VOID); + } + + if (o.getType().getType() instanceof JvmTypeParameter) { + return new SimpleEntry<>(o.getName(), "java.lang.Object"); + } else { + return new SimpleEntry<>(o.getName(), o.getType().getQualifiedName()); + } + } + + public static Optional arithOpFromOperator( + Operator operator) { + switch (operator) { + case PLUS: + return Optional.of(CrySLArithmeticConstraint.ArithOp.p); + // case TIMES: return Optional.of(ArithOp.t); /* Only in Syntax yet */ + // case DIVIDE: return Optional.of(ArithOp.g); /* Only in Syntax yet */ + case MINUS: + return Optional.of(CrySLArithmeticConstraint.ArithOp.n); + case MODULO: + return Optional.of(CrySLArithmeticConstraint.ArithOp.m); + default: + return Optional.empty(); + } + } + + public static Optional logOpFromOperator(Operator operator) { + switch (operator) { + case AND: + return Optional.of(CrySLConstraint.LogOps.and); + case OR: + return Optional.of(CrySLConstraint.LogOps.or); + case IMPLY: + return Optional.of(CrySLConstraint.LogOps.implies); + // case NOT: return Optional.of(LogOps.not); /* Only in Syntax yet */ + // case EQUAL: return Optional.of(LogOps.eq); /* unused enum item */ + default: + return Optional.empty(); + } + } + + public static Optional compOpFromOperator(Operator operator) { + switch (operator) { + case EQUAL: + return Optional.of(CrySLComparisonConstraint.CompOp.eq); + case UNEQUAL: + return Optional.of(CrySLComparisonConstraint.CompOp.neq); + case LESS: + return Optional.of(CrySLComparisonConstraint.CompOp.l); + case LESS_OR_EQUAL: + return Optional.of(CrySLComparisonConstraint.CompOp.le); + case GREATER: + return Optional.of(CrySLComparisonConstraint.CompOp.g); + case GREATER_OR_EQUAL: + return Optional.of(CrySLComparisonConstraint.CompOp.ge); + default: + return Optional.empty(); + } + } + + public static boolean isArithmeticExpression(Constraint constraint) { + return arithOpFromOperator(constraint.getOp()).isPresent(); + } + + public static boolean isLogicExpression(Constraint constraint) { + return logOpFromOperator(constraint.getOp()).isPresent(); + } + + public static boolean isComparisonExpression(Constraint constraint) { + return compOpFromOperator(constraint.getOp()).isPresent(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CryslException.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CryslException.java new file mode 100644 index 00000000..39dfcb6d --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/CryslException.java @@ -0,0 +1,8 @@ +package de.darmstadt.tu.crossing.parsing; + +public class CrySLException extends Exception { + + public CrySLException(String message) { + super(message); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/ExceptionsReader.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/ExceptionsReader.java new file mode 100644 index 00000000..e90f01eb --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/ExceptionsReader.java @@ -0,0 +1,30 @@ +package de.darmstadt.tu.crossing.parsing; + +import de.darmstadt.tu.crossing.crySL.EventsBlock; +import de.darmstadt.tu.crossing.crySL.LabeledMethodCall; +import de.darmstadt.tu.crossing.rule.CrySLExceptionConstraint; +import de.darmstadt.tu.crossing.rule.CrySLMethod; +import java.util.Collection; +import java.util.stream.Collectors; + +/** Helper class to derive {@link CrySLExceptionConstraint}'s from the events. */ +public abstract class ExceptionsReader { + public static Collection getExceptionConstraints( + EventsBlock eventsBlock) { + return eventsBlock.getEvents().stream() + .filter(event -> event instanceof LabeledMethodCall) + .map(event -> (LabeledMethodCall) event) + .flatMap( + meth -> + CrySLReaderUtils.resolveExceptionsStream(meth.getException()) + .map( + exception -> { + CrySLMethod method = + CrySLReaderUtils.toCrySLMethod( + meth.getMethod()); + return new CrySLExceptionConstraint( + method, exception); + })) + .collect(Collectors.toList()); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/StateMachineGraphBuilder.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/StateMachineGraphBuilder.java new file mode 100644 index 00000000..4d145d59 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/parsing/StateMachineGraphBuilder.java @@ -0,0 +1,258 @@ +package de.darmstadt.tu.crossing.parsing; + +import de.darmstadt.tu.crossing.crySL.Event; +import de.darmstadt.tu.crossing.crySL.Order; +import de.darmstadt.tu.crossing.crySL.OrderOperator; +import de.darmstadt.tu.crossing.crySL.Primary; +import de.darmstadt.tu.crossing.rule.CrySLMethod; +import de.darmstadt.tu.crossing.rule.FiniteStateMachine; +import de.darmstadt.tu.crossing.rule.StateMachineGraph; +import de.darmstadt.tu.crossing.rule.StateNode; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +/** + * This class will build a {@link FiniteStateMachine} for a given ORDER expression from crysl rules. + * + * @author marvinvogel + */ +public class StateMachineGraphBuilder { + + private final Order order; + private final StateMachineGraph result; + private final Collection events; + private final Collection allMethods = new HashSet<>(); + + public static StateMachineGraph buildSMG(final Order order, final Collection events) { + return (new StateMachineGraphBuilder(order, events)).buildSMG(); + } + + protected StateMachineGraphBuilder(final Order order, final Collection events) { + this.order = order; + this.events = events; + this.result = new StateMachineGraph(); + } + + protected StateMachineGraph buildSMG() { + StateNode initialNode = new StateNode("-1", true, false); + this.result.addNode(initialNode); + SubStateMachine subSmg = buildSubSMG(this.order, Collections.singleton(initialNode)); + subSmg.getEndNodes().parallelStream().forEach(StateNode::makeAccepting); + return this.result; + } + + /** Helper class to store a {@link Collection} of endNodes and startNodes. */ + private static class SubStateMachine { + + private final Collection startNodes; + private final Collection endNodes; + + public SubStateMachine(final StateNode startNode, final StateNode endNode) { + this(Collections.singleton(startNode), Collections.singleton(endNode)); + } + + public SubStateMachine( + final Collection startNodes, final Collection endNodes) { + this.startNodes = startNodes; + this.endNodes = endNodes; + } + + public Collection getStartNodes() { + return this.startNodes; + } + + public Collection getEndNodes() { + return this.endNodes; + } + } + + /** + * This method builds a {@link SubStateMachine} equivalent to the given {@link Order}. + * + *

It is called recursively For a collection of start nodes, which must already be added to + * the {@link StateMachineGraph} result. + * + * @param order The CrySL {@link Order} Expression to build the {@link SubStateMachine} for. + * @param startNodes Set of nodes used as the startNodes of this {@link SubStateMachine}. + * @return the {@link SubStateMachine} representing this {@link Order} Expression + *

Having a look at the Crysl Xtext definition for the Order section, we have ----------- + * + * Sequence returns Order: + * Alternative ({Order.left=current} op=SequenceOperator right=Alternative)* + * ; + *

+ * enum SequenceOperator returns OrderOperator: + * SEQUENCE = ',' + * ; + *

+ *

+ * Alternative returns Order: + * Cardinality ({Order.left=current} op=AlternativeOperator right=Cardinality)* + * ; + *

+ *

+ * enum AlternativeOperator returns OrderOperator: + * ALTERNATIVE = '|' + * ; + *

+ *

+ * Cardinality returns Order: + * Primary ({Order.left=current} op = CardinalityOperator)? + * ; + *

+ *

+ * enum CardinalityOperator returns OrderOperator: + * ZERO_OR_MORE = '*' | ONE_OR_MORE = '+' | ZERO_OR_ONE = '?' + * ; + *

+ *

+ * Primary returns Order: + * {Primary} event = [Event] + * | '(' Order ')' + * ; + *

+ *
----------- + *

Based on this definition, the method will build the StateMachine from the Order + * section. + *

This is done by recursively building a sub-StateMachine and connecting it with given + * start Nodes and returned end Nodes according to the specific OrderOperator. + *

Therefore, consider the following cases + *

1. Order instanceof Primary: In this case, we create a new Node and add a transition + * for the Event from each start Node to the newly created Node. Finally, we return a + * SubStateMachine where the newly created node is the start and end Node. + *

2. OrderOperator == SEQUENCE: The left-side should occur before the right-side. We + * therefore recursively build the sub-StateMachine of the left-side with the given start + * Nodes saving the returned end Nodes. We then build the sub-StateMachine of the right-side + * giving it the left-side's end Nodes as start Nodes. Finally, we return the startNodes of + * the left-side's start Nodes as our start Nodes and the end Nodes of the right-side's + * sub-StateMachine as our end Nodes. + *

3. OrderOperator == ALTERNATIVE: Either the left-side or the right-side should occur. + * We therefore build both sub-StateMachines with our start Nodes as start Nodes. Finally, + * we return the aggregate of both start Nodes as our startNodes and the aggregates of both + * end Nodes as our end Nodes. + *

4. OrderOperator == ZERO_OR_ONE: The Event can occur or be skipped. We therefore build + * the sub-StateMachine (only the left-side is present) with our start Nodes as start Nodes. + * Finally, we return the returned start Nodes as our start Nodes and the returned Nodes end + * Nodes (event occurs) and our start nodes (event skipped) as end Nodes. + *

5. OrderOperator == ONE_OR_MORE: The Event can occur once or multiple times. We + * therefore build the sub-StateMachine (only the left-side is present) with our start Nodes + * as start Nodes and save the returned end Nodes. We then duplicate the transitions from + * each given start Node to the start node of the Sub-StateMachine, but not with the given + * start Node as origin, but each end Node of the Sub-StateMachine, this creates the desired + * loop. Finally, we return the returned start and end Nodes. + *

5. OrderOperator == ZERO_OR_MORE: This can be seen as (Order)*?. We therefore proceed + * as in ONE_OR_MORE but additionally return the given start Nodes as end Nodes as well as + * in ZERO_OR_ONE. + */ + private SubStateMachine buildSubSMG(final Order order, final Collection startNodes) { + + if (order == null) { + // This is the case if the ORDER section was omitted. + // It implies, that any method may be called in any sequence. + // We therefore create a Node and a transition from all startNodes + // to this node and a loop from the node to itself. + StateNode node = this.result.createNewNode(); + node.setAccepting(true); + Collection label = new HashSet<>(retrieveAllMethodsFromEvents()); + + for (StateNode startNode : startNodes) { + this.result.createNewEdge(label, startNode, node); + } + + this.result.createNewEdge(label, node, node); + return new SubStateMachine(Collections.singleton(node), startNodes); + } + + if (order instanceof Primary) { + Event event = ((Primary) order).getEvent(); + StateNode node = this.result.createNewNode(); + Collection label = CrySLReaderUtils.resolveEventToCryslMethods(event); + + for (StateNode startNode : startNodes) { + this.result.createNewEdge(label, startNode, node); + } + + return new SubStateMachine(node, node); + } + + Collection end = new HashSet<>(); + Collection start = new HashSet<>(); + + final SubStateMachine left; + final SubStateMachine right; + + switch (order.getOp()) { + case SEQUENCE: + left = buildSubSMG(order.getLeft(), startNodes); + right = buildSubSMG(order.getRight(), left.getEndNodes()); + start.addAll(left.getStartNodes()); + end.addAll(right.getEndNodes()); + + for (StateNode node : startNodes) { + if (left.getEndNodes().contains(node)) { + start.addAll(right.getStartNodes()); + } + } + + break; + case ALTERNATIVE: + left = buildSubSMG(order.getLeft(), startNodes); + right = buildSubSMG(order.getRight(), startNodes); + start.addAll(left.getStartNodes()); + start.addAll(right.getStartNodes()); + end.addAll(left.getEndNodes()); + end.addAll(right.getEndNodes()); + // TODO For some reason, this part removes loops in accepting states + /* reduce all end nodes without outgoing edges to one end node + * Set endNodesWithOutgoingEdges = + * this.result.getEdges().parallelStream() + * .map(edge -> edge.from()).filter(node -> + * end.contains(node)).collect(Collectors.toSet()); + * if (endNodesWithOutgoingEdges.size() < end.size() - 1) { + * end.removeAll(endNodesWithOutgoingEdges); + * StateNode aggrNode = this.result.aggregateNodesToOneNode(end, + * end.iterator().next()); + * end.clear(); + * end.add(aggrNode); + * end.addAll(endNodesWithOutgoingEdges); + * } + */ + break; + case ONE_OR_MORE: + case ZERO_OR_MORE: + case ZERO_OR_ONE: + left = buildSubSMG(order.getLeft(), startNodes); + start.addAll(left.getStartNodes()); + end.addAll(left.getEndNodes()); + if (order.getOp() == OrderOperator.ZERO_OR_MORE + || order.getOp() == OrderOperator.ONE_OR_MORE) { + startNodes.stream() + .map(this.result::getAllOutgoingEdges) + .flatMap(Collection::stream) + .filter(edge -> left.getStartNodes().contains(edge.getRight())) + .forEach( + edge -> + left.getEndNodes() + .forEach( + endNode -> + this.result.createNewEdge( + edge.getLabel(), + endNode, + edge.getRight()))); + } + if (order.getOp() == OrderOperator.ZERO_OR_MORE + || order.getOp() == OrderOperator.ZERO_OR_ONE) { + end.addAll(startNodes); + } + break; + } + return new SubStateMachine(start, end); + } + + private Collection retrieveAllMethodsFromEvents() { + if (this.allMethods.isEmpty()) + this.allMethods.addAll(CrySLReaderUtils.resolveEventsToCryslMethods(this.events)); + return this.allMethods; + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLArithmeticConstraint.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLArithmeticConstraint.java new file mode 100644 index 00000000..ab27d821 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLArithmeticConstraint.java @@ -0,0 +1,87 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.ArrayList; +import java.util.List; + +public class CrySLArithmeticConstraint extends CrySLLiteral { + + public enum ArithOp { + p, + n, + m + } + + /* p = + + * n = - + * m = % + */ + + private final ArithOp operator; + private final ICrySLPredicateParameter left; + private final ICrySLPredicateParameter right; + + public CrySLArithmeticConstraint( + ICrySLPredicateParameter l, ICrySLPredicateParameter r, ArithOp op) { + left = l; + right = r; + operator = op; + } + + /** + * @return the operator + */ + public ArithOp getOperator() { + return operator; + } + + /** + * @return the left + */ + public ICrySLPredicateParameter getLeft() { + return left; + } + + /** + * @return the right + */ + public ICrySLPredicateParameter getRight() { + return right; + } + + public String toString() { + return left + + " " + + (operator.equals(ArithOp.p) ? "+" : (operator.equals(ArithOp.m) ? "%" : "-")) + + " " + + right; + } + + @Override + public List getInvolvedVarNames() { + List varNames = new ArrayList<>(); + String name = left.getName(); + if (!isIntOrBoolean(name)) { + varNames.add(name); + } + + name = right.getName(); + if (!isIntOrBoolean(name)) { + varNames.add(name); + } + return varNames; + } + + private boolean isIntOrBoolean(String name) { + try { + Integer.parseInt(name); + return true; + } catch (NumberFormatException ex) { + return name.equalsIgnoreCase("false") || name.equalsIgnoreCase("true"); + } + } + + @Override + public String getName() { + return toString(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLComparisonConstraint.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLComparisonConstraint.java new file mode 100644 index 00000000..9b550b8d --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLComparisonConstraint.java @@ -0,0 +1,80 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.List; + +public class CrySLComparisonConstraint extends CrySLLiteral { + + public enum CompOp { + l, + g, + le, + ge, + eq, + neq + } + + private final CompOp operator; + private final CrySLArithmeticConstraint left; + private final CrySLArithmeticConstraint right; + + public CrySLComparisonConstraint( + CrySLArithmeticConstraint l, CrySLArithmeticConstraint r, CompOp op) { + left = l; + right = r; + operator = op; + } + + public String toString() { + return left + " " + getOperatorString() + " " + right; + } + + private String getOperatorString() { + switch (operator) { + case l: + return "<"; + case le: + return "<="; + case g: + return ">"; + case ge: + return ">="; + case neq: + return "!="; + default: + return "="; + } + } + + /** + * @return the operator + */ + public CompOp getOperator() { + return operator; + } + + /** + * @return the left + */ + public CrySLArithmeticConstraint getLeft() { + return left; + } + + /** + * @return the right + */ + public CrySLArithmeticConstraint getRight() { + return right; + } + + @Override + public List getInvolvedVarNames() { + List varNames = left.getInvolvedVarNames(); + varNames.addAll(right.getInvolvedVarNames()); + return varNames; + } + + @Override + public String getName() { + return toString(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLCondPredicate.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLCondPredicate.java new file mode 100644 index 00000000..d49dfdc1 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLCondPredicate.java @@ -0,0 +1,52 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Collection; +import java.util.List; + +public class CrySLCondPredicate extends CrySLPredicate { + + private final Collection conditionalNodes; + + public CrySLCondPredicate( + ICrySLPredicateParameter baseObj, + String name, + List parameters, + Boolean negated, + Collection nodes) { + this(baseObj, name, parameters, negated, nodes, null); + } + + public CrySLCondPredicate( + ICrySLPredicateParameter baseObj, + String name, + List parameters, + Boolean negated, + Collection nodes, + ISLConstraint constraint) { + super(baseObj, name, parameters, negated, constraint); + this.conditionalNodes = nodes; + } + + /** + * @return the conditionalMethods + */ + public Collection getConditionalMethods() { + return conditionalNodes; + } + + @Override + public boolean equals(Object obj) { + if (!super.equals(obj)) return false; + + if (!(obj instanceof CrySLCondPredicate)) return false; + + CrySLCondPredicate other = (CrySLCondPredicate) obj; + if (!getConditionalMethods().equals(other.getConditionalMethods())) return false; + + return true; + } + + public String toString() { + return super.toString() + " after " + conditionalNodes; + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLConstraint.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLConstraint.java new file mode 100644 index 00000000..e950257a --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLConstraint.java @@ -0,0 +1,64 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.List; + +public class CrySLConstraint implements ISLConstraint { + + public enum LogOps { + and, + or, + implies, + eq + } + + private final LogOps operator; + private final ISLConstraint left; + private final ISLConstraint right; + + public CrySLConstraint(ISLConstraint l, ISLConstraint r, LogOps op) { + left = l; + right = r; + operator = op; + } + + /** + * @return the operator return operator; + */ + public LogOps getOperator() { + return operator; + } + + /** + * @return the left + */ + public ISLConstraint getLeft() { + return left; + } + + /** + * @return the right + */ + public ISLConstraint getRight() { + return right; + } + + public String toString() { + StringBuilder constraintSB = new StringBuilder(); + constraintSB.append(left.toString()); + constraintSB.append(operator); + constraintSB.append(right.toString()); + return constraintSB.toString(); + } + + @Override + public List getInvolvedVarNames() { + List varNames = left.getInvolvedVarNames(); + varNames.addAll(right.getInvolvedVarNames()); + return varNames; + } + + @Override + public String getName() { + return toString(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLException.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLException.java new file mode 100644 index 00000000..29fdd28d --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLException.java @@ -0,0 +1,28 @@ +package de.darmstadt.tu.crossing.rule; + +/** Helper Class to store an {@link Exception} as a String. */ +public class CrySLException { + + private final String exception; + + /** + * Construct a {@link CrySLException} from the fully qualified classname of the {@link + * Exception} to store. + * + * @param exception the exception's name + */ + public CrySLException(String exception) { + this.exception = exception; + } + + /** + * @return The fully qualified classname of the stored {@link Exception}. + */ + public String getException() { + return exception; + } + + public String toString() { + return String.format("%s(%s)", this.getClass().getName(), this.exception); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLExceptionConstraint.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLExceptionConstraint.java new file mode 100644 index 00000000..d1114ca5 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLExceptionConstraint.java @@ -0,0 +1,61 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Collections; +import java.util.List; + +/** + * Constraint expressing, that a {@link CrySLMethod} throws an {@link CrySLException}, that must be + * caught. + */ +public class CrySLExceptionConstraint implements ISLConstraint { + + /** The Method throwing the Exception. */ + private final CrySLMethod method; + + /** The Exception thrown by the Method. */ + private final CrySLException exception; + + /** + * Construct the {@link CrySLExceptionConstraint} given the method and the exception thrown + * thereby. + * + * @param method Method that throws the Exception. + * @param exception Exception that thrown by the Method. + */ + public CrySLExceptionConstraint(CrySLMethod method, CrySLException exception) { + this.method = method; + this.exception = exception; + } + + /** + * Returns the Method throwing the Exception. + * + * @return The Method throwing the Exception. + */ + public CrySLMethod getMethod() { + return method; + } + + /** + * Returns the Exception thrown by the Exception. + * + * @return The Exception thrown by the Exception. + */ + public CrySLException getException() { + return exception; + } + + public String toString() { + return String.format("%s(%s, %s)", this.getClass().getName(), getMethod(), getException()); + } + + @Override + public List getInvolvedVarNames() { + return Collections.emptyList(); + } + + @Override + public String getName() { + return toString(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLForbiddenMethod.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLForbiddenMethod.java new file mode 100644 index 00000000..4ed81032 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLForbiddenMethod.java @@ -0,0 +1,55 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Collection; + +public class CrySLForbiddenMethod { + + private final CrySLMethod method; + private final Collection alternatives; + + public CrySLForbiddenMethod(CrySLMethod method, Collection alternatives) { + this.method = method; + this.alternatives = alternatives; + } + + public CrySLMethod getMethod() { + return method; + } + + public Collection getAlternatives() { + return alternatives; + } + + @Override + public String toString() { + final StringBuilder forbiddenMethod = new StringBuilder(); + forbiddenMethod.append(method.toString()); + if (!alternatives.isEmpty()) { + forbiddenMethod.append(" Alternatives: "); + } + + for (CrySLMethod meth : alternatives) { + forbiddenMethod.append(meth.toString()); + } + + return forbiddenMethod.toString(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof CrySLForbiddenMethod)) { + return false; + } + + CrySLForbiddenMethod other = (CrySLForbiddenMethod) obj; + if (!method.equals(other.getMethod())) { + return false; + } + + return alternatives.equals(other.getAlternatives()); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLLiteral.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLLiteral.java new file mode 100644 index 00000000..4f328d81 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLLiteral.java @@ -0,0 +1,6 @@ +package de.darmstadt.tu.crossing.rule; + +public abstract class CrySLLiteral implements ISLConstraint { + + protected CrySLLiteral() {} +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLMethod.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLMethod.java new file mode 100644 index 00000000..7efc0850 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLMethod.java @@ -0,0 +1,121 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.List; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +public class CrySLMethod implements ICrySLPredicateParameter { + + public static final String VOID = "void"; + public static final String ANY_TYPE = "AnyType"; + public static final String NO_NAME = "_"; + + private final String methodName; + private final String declaringClassName; + private final Entry retObject; + + /** + * List of Parameters, where a Parameter is an {@link Entry} of Name and Type, both as {@link + * String}. + */ + private final List> parameters; + + public CrySLMethod( + String methodName, + String declaringClassName, + List> parameters, + Entry retObject) { + this.methodName = methodName; + this.declaringClassName = declaringClassName; + this.parameters = parameters; + this.retObject = retObject; + } + + /** + * @return the FQ methodName + */ + public String getMethodName() { + return methodName; + } + + /** + * @return the short methodName + */ + public String getShortMethodName() { + return methodName.substring(methodName.lastIndexOf(".") + 1); + } + + public String getDeclaringClassName() { + return declaringClassName; + } + + /** + * @return the parameters + */ + public List> getParameters() { + return parameters; + } + + public Entry getRetObject() { + return retObject; + } + + public String toString() { + return getName(); + } + + public String getSignature() { + return getMethodName() + + "(" + + parameters.stream() + .map(param -> String.format("%s", param.getValue())) + .collect(Collectors.joining(", ")) + + ")"; + } + + @Override + public String getName() { + StringBuilder stmntBuilder = new StringBuilder(); + String returnValue = retObject.getKey(); + if (!"_".equals(returnValue)) { + stmntBuilder.append(returnValue); + stmntBuilder.append(" = "); + } + + stmntBuilder.append(this.methodName); + stmntBuilder.append("("); + + stmntBuilder.append( + parameters.stream() + .map(param -> String.format("%s %s", param.getValue(), param.getKey())) + .collect(Collectors.joining(", "))); + + stmntBuilder.append(");"); + return stmntBuilder.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((methodName == null) ? 0 : methodName.hashCode()); + result = prime * result + ((parameters == null) ? 0 : parameters.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof CrySLMethod)) { + return false; + } + CrySLMethod other = (CrySLMethod) obj; + return this.getMethodName().equals(other.getMethodName()) + && parameters.equals(other.parameters); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLObject.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLObject.java new file mode 100644 index 00000000..dff929dd --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLObject.java @@ -0,0 +1,64 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Arrays; + +public class CrySLObject implements ICrySLPredicateParameter { + + private final String varName; + private final String javaType; + private final CrySLSplitter splitter; + + public CrySLObject(String name, String type) { + this(name, type, null); + } + + public CrySLObject(String name, String type, CrySLSplitter part) { + varName = name; + javaType = type; + splitter = part; + } + + /** + * @return the varName + */ + public String getVarName() { + return varName; + } + + /** + * @return the splitter + */ + public CrySLSplitter getSplitter() { + return splitter; + } + + @Override + public boolean equals(Object other) { + if (other == this) return true; + if (other == null) return false; + if (!(other instanceof CrySLObject)) return false; + CrySLObject object = (CrySLObject) other; + return this.getJavaType().equals(object.getJavaType()) + && this.getName().equals(object.getName()) + && (this.getSplitter() == null || this.getSplitter().equals(object.getSplitter())); + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[] {varName, javaType, splitter}); + } + + @Override + public String toString() { + return javaType + " " + varName + ((splitter != null) ? splitter.toString() : ""); + } + + @Override + public String getName() { + return varName; + } + + public String getJavaType() { + return javaType; + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLPredicate.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLPredicate.java new file mode 100644 index 00000000..f0e7fd7f --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLPredicate.java @@ -0,0 +1,156 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +public class CrySLPredicate extends CrySLLiteral { + + protected final ICrySLPredicateParameter baseObject; + protected final String predName; + protected final List parameters; + protected final boolean negated; + protected final ISLConstraint constraint; + + public CrySLPredicate( + ICrySLPredicateParameter baseObject, + String name, + List parameters, + Boolean negated) { + this(baseObject, name, parameters, negated, null); + } + + public CrySLPredicate( + ICrySLPredicateParameter baseObject, + String name, + List parameters, + Boolean negated, + ISLConstraint constraint) { + this.baseObject = baseObject; + this.predName = name; + this.parameters = parameters; + this.negated = negated; + this.constraint = constraint; + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[] {predName, parameters.size(), negated}); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof CrySLPredicate)) { + return false; + } + + CrySLPredicate other = (CrySLPredicate) obj; + if (baseObject == null) { + if (other.getBaseObject() != null) return false; + } else if (!baseObject.equals(other.getBaseObject())) { + return false; + } + + if (predName == null) { + if (other.getPredName() != null) return false; + } else if (!predName.equals(other.getPredName())) { + return false; + } + + if (parameters == null) { + if (other.getParameters() != null) return false; + } else if (parameters.size() != other.getParameters().size()) { + return false; + } + + return negated == other.isNegated(); + } + + /** + * @return the baseObject + */ + public ICrySLPredicateParameter getBaseObject() { + return baseObject; + } + + /** + * @return the predName + */ + public String getPredName() { + return predName; + } + + /** + * @return the optConstraint + */ + public Optional getConstraint() { + return Optional.ofNullable(constraint); + } + + /** + * @return the parameters + */ + public List getParameters() { + return parameters; + } + + /** + * @return the negated + */ + public Boolean isNegated() { + return negated; + } + + @Override + public String toString() { + StringBuilder predSB = new StringBuilder(); + if (negated) predSB.append("!"); + predSB.append(predName); + predSB.append("("); + predSB.append(parameters.stream().map(Object::toString).collect(Collectors.joining(", "))); + predSB.append(")"); + + return predSB.toString(); + } + + @Override + public List getInvolvedVarNames() { + List varNames = new ArrayList<>(); + if (Arrays.asList(new String[] {"neverTypeOf", "instanceOf"}).contains(predName)) { + varNames.add(parameters.get(0).getName()); + } else { + for (ICrySLPredicateParameter var : parameters) { + if (!("_".equals(var.getName()) + || "this".equals(var.getName()) + || var instanceof CrySLMethod)) { + varNames.add(var.getName()); + } + } + } + if (getBaseObject() != null) varNames.add(getBaseObject().getName()); + return varNames; + } + + public CrySLPredicate invertNegation() { + return new CrySLPredicate(baseObject, predName, parameters, !negated); + } + + public CrySLPredicate toNormalCrySLPredicate() { + return new CrySLPredicate(baseObject, predName, parameters, negated, constraint); + } + + @Override + public String getName() { + if (parameters.size() == 1) { + return parameters.get(0).getName(); + } else { + return ""; + } + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLRule.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLRule.java new file mode 100644 index 00000000..9c434634 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLRule.java @@ -0,0 +1,171 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.Map; + +public class CrySLRule { + + private final String className; + + private final Collection> objects; + + private final Collection forbiddenMethods; + + private final Collection events; + + private final StateMachineGraph usagePattern; + + private final Collection constraints; + + private final Collection predicates; + + private final Collection negatedPredicates; + + public CrySLRule( + String className, + Collection> objects, + Collection forbiddenMethods, + Collection events, + StateMachineGraph usagePattern, + Collection constraints, + Collection predicates, + Collection negatedPredicates) { + this.className = className; + this.objects = objects; + this.forbiddenMethods = forbiddenMethods; + this.events = events; + this.usagePattern = usagePattern; + this.constraints = constraints; + this.predicates = predicates; + this.negatedPredicates = negatedPredicates; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof CrySLRule) { + return ((CrySLRule) obj).getClassName().equals(className); + } + return false; + } + + @Override + public int hashCode() { + return 31 * className.hashCode(); + } + + /** + * @return the className + */ + public String getClassName() { + return className; + } + + /** + * @return the objects + */ + public Collection> getObjects() { + return objects; + } + + /** + * @return the forbiddenMethods + */ + public Collection getForbiddenMethods() { + return forbiddenMethods; + } + + /** + * @return the events + */ + public Collection getEvents() { + return events; + } + + /** + * @return the usagePattern + */ + public StateMachineGraph getUsagePattern() { + return usagePattern; + } + + /** + * @return the constraints + */ + public Collection getConstraints() { + return constraints; + } + + /** + * @return the predicates + */ + public Collection getPredicates() { + return predicates; + } + + /** + * @return the negated predicates + */ + public Collection getNegatedPredicates() { + return negatedPredicates; + } + + /** + * @return the constraints + */ + public Collection getRequiredPredicates() { + Collection requires = new LinkedList<>(); + for (ISLConstraint con : constraints) { + if (con instanceof CrySLPredicate) { + requires.add((CrySLPredicate) con); + } + } + return requires; + } + + @Override + public String toString() { + StringBuilder outputSB = new StringBuilder(); + + outputSB.append(this.className); + + outputSB.append("\nforbiddenMethods:"); + for (CrySLForbiddenMethod forMethSig : this.forbiddenMethods) { + outputSB.append(forMethSig); + outputSB.append(", "); + } + + outputSB.append("\nEvents:"); + for (CrySLMethod method : events) { + outputSB.append(method); + outputSB.append(", "); + } + + outputSB.append("\nUsage Pattern:"); + outputSB.append(this.usagePattern); + + outputSB.append("\nConstraints:"); + for (ISLConstraint constraint : this.constraints) { + outputSB.append(constraint); + outputSB.append(", "); + } + + if (this.predicates != null) { + outputSB.append("\nPredicates:"); + for (CrySLPredicate predicate : this.predicates) { + outputSB.append(predicate); + outputSB.append(", "); + } + } + + if (this.negatedPredicates != null) { + outputSB.append("\nNegated predicates:"); + for (CrySLPredicate predicate : this.negatedPredicates) { + outputSB.append(predicate); + outputSB.append(", "); + } + } + + return outputSB.toString(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLSplitter.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLSplitter.java new file mode 100644 index 00000000..f32a97c4 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLSplitter.java @@ -0,0 +1,32 @@ +package de.darmstadt.tu.crossing.rule; + +public class CrySLSplitter { + + private final int index; + private final String split; + + public CrySLSplitter(int ind, String spl) { + this.index = ind; + this.split = spl; + } + + public int getIndex() { + return index; + } + + public String getSplitter() { + return split; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CrySLSplitter)) return false; + + CrySLSplitter splitter = (CrySLSplitter) other; + return this.index == splitter.getIndex() && this.split.equals(splitter.getSplitter()); + } + + public String toString() { + return ".split(" + split + ")[" + index + "]"; + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLValueConstraint.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLValueConstraint.java new file mode 100644 index 00000000..989f69e8 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLValueConstraint.java @@ -0,0 +1,60 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.ArrayList; +import java.util.List; + +public class CrySLValueConstraint extends CrySLLiteral { + + CrySLObject var; + List valueRange; + + public CrySLValueConstraint(CrySLObject name, List values) { + var = name; + valueRange = values; + } + + /** + * @return the varName + */ + public String getVarName() { + return var.getVarName(); + } + + /** + * @return the varName + */ + public CrySLObject getVar() { + return var; + } + + /** + * @return the valueRange + */ + public List getValueRange() { + return valueRange; + } + + public String toString() { + StringBuilder vCSB = new StringBuilder(); + vCSB.append("VC:"); + vCSB.append(var); + vCSB.append(" - "); + for (String value : valueRange) { + vCSB.append(value); + vCSB.append(","); + } + return vCSB.toString(); + } + + @Override + public List getInvolvedVarNames() { + List varNames = new ArrayList<>(); + varNames.add(var.getVarName()); + return varNames; + } + + @Override + public String getName() { + return toString(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/FiniteStateMachine.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/FiniteStateMachine.java new file mode 100644 index 00000000..2f16e4b2 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/FiniteStateMachine.java @@ -0,0 +1,11 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Collection; + +public interface FiniteStateMachine { + Transition getInitialTransition(); + + Collection getAcceptingStates(); + + Collection> getAllTransitions(); +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ICrySLPredicateParameter.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ICrySLPredicateParameter.java new file mode 100644 index 00000000..6eca9d59 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ICrySLPredicateParameter.java @@ -0,0 +1,6 @@ +package de.darmstadt.tu.crossing.rule; + +public interface ICrySLPredicateParameter { + + String getName(); +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ISLConstraint.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ISLConstraint.java new file mode 100644 index 00000000..14d009b6 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/ISLConstraint.java @@ -0,0 +1,8 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.List; + +public interface ISLConstraint extends ICrySLPredicateParameter { + + List getInvolvedVarNames(); +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraph.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraph.java new file mode 100644 index 00000000..83779cb0 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraph.java @@ -0,0 +1,189 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.stream.Collectors; + +public final class StateMachineGraph implements FiniteStateMachine { + + private StateNode startNode; + private final Collection nodes; + private final Collection edges; + private final Collection initialEdges; + private int nodeNameCounter = 0; + + public StateMachineGraph() { + nodes = new HashSet<>(); + edges = new ArrayList<>(); + initialEdges = new ArrayList<>(); + } + + public StateNode createNewNode() { + StateNode node = new StateNode(String.valueOf(this.nodeNameCounter++), false, false); + this.nodes.add(node); + return node; + } + + public boolean createNewEdge(Collection methods, StateNode left, StateNode right) { + return this.addEdge(new TransitionEdge(methods, left, right)); + } + + private Boolean addEdge(TransitionEdge edge) { + final StateNode right = edge.getRight(); + final StateNode left = edge.getLeft(); + if (!(nodes.parallelStream().anyMatch(e -> e.equals(left)) + || nodes.parallelStream().anyMatch(e -> e.equals(right)))) { + return false; + } + if (edges.contains(edge)) { + return false; + } + edges.add(edge); + + if (left.isInitialState()) { + initialEdges.add(edge); + } + + return true; + } + + public void wrapUpCreation() { + getAcceptingStates().parallelStream() + .forEach( + e -> { + e.setHopsToAccepting(0); + updateHops(e); + }); + } + + public Collection getAllOutgoingEdges(StateNode node) { + return edges.parallelStream() + .filter(edge -> edge.from().equals(node)) + .collect(Collectors.toSet()); + } + + public void addAllOutgoingEdgesFromOneNodeToOtherNodes( + StateNode node, Collection otherNodes) { + Collection edgesFromNode = + edges.parallelStream() + .filter(e -> node.equals(e.from())) + .collect(Collectors.toList()); + otherNodes.forEach( + otherNode -> + edgesFromNode.forEach( + edge -> + this.createNewEdge( + edge.getLabel(), otherNode, edge.getLeft()))); + } + + public StateNode aggregateNodesToOneNode(Collection endNodes, StateNode newNode) { + this.aggregateNodesToOtherNodes(endNodes, Collections.singleton(newNode)); + return newNode; + } + + public Collection aggregateNodesToOtherNodes( + Collection nodesToAggr, Collection startNodes) { + Collection edgesToAnyAggrNode = + edges.parallelStream() + .filter(e -> nodesToAggr.contains(e.to())) + .collect(Collectors.toList()); + // Add new edges to newNode instead of Aggr Node + startNodes.forEach( + node -> + edgesToAnyAggrNode.forEach( + edgeToAggrNode -> + this.createNewEdge( + edgeToAggrNode.getLabel(), + edgeToAggrNode.getLeft(), + node))); + nodesToAggr.removeAll(startNodes); + removeNodesWithAllEdges(nodesToAggr); + return startNodes; + } + + private void removeNodesWithAllEdges(Collection nodesToRemove) { + nodesToRemove.forEach(this::removeNodeWithAllEdges); + } + + private void removeNodeWithAllEdges(StateNode node) { + removeAllEdgesHavingNode(node); + nodes.remove(node); + } + + private void removeAllEdgesHavingNode(StateNode node) { + Collection filteredEdges = + edges.parallelStream() + .filter(e -> node.equals(e.to()) || node.equals(e.from())) + .collect(Collectors.toList()); + edges.removeAll(filteredEdges); + } + + private void updateHops(StateNode node) { + int newPath = node.getHopsToAccepting() + 1; + getAllTransitions().parallelStream() + .forEach( + e -> { + StateNode theNewRight = e.getLeft(); + if (e.getRight().equals(node) + && theNewRight.getHopsToAccepting() > newPath) { + theNewRight.setHopsToAccepting(newPath); + updateHops(theNewRight); + } + }); + } + + public Boolean addNode(StateNode node) { + if (node.isInitialState()) { + this.startNode = node; + } + return nodes.parallelStream().anyMatch(n -> n.getName().equals(node.getName())) + ? false + : nodes.add(node); + } + + public String toString() { + StringBuilder graphSB = new StringBuilder(); + for (StateNode node : nodes) { + graphSB.append(node.toString()); + graphSB.append(System.lineSeparator()); + } + + for (TransitionEdge te : edges) { + graphSB.append(te.toString()); + graphSB.append(System.lineSeparator()); + } + + return graphSB.toString(); + } + + public Collection getNodes() { + return nodes; + } + + public StateNode getStartNode() { + return startNode; + } + + public Collection getEdges() { + return edges; + } + + public TransitionEdge getInitialTransition() { + throw new UnsupportedOperationException( + "There is no single initial transition. Use getInitialTransitions()"); + } + + public Collection getInitialTransitions() { + return initialEdges; + } + + public Collection getAcceptingStates() { + return nodes.parallelStream().filter(StateNode::getAccepting).collect(Collectors.toList()); + } + + public Collection getAllTransitions() { + return getEdges(); + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraphReader.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraphReader.java new file mode 100644 index 00000000..13bfc1eb --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraphReader.java @@ -0,0 +1,23 @@ +package de.darmstadt.tu.crossing.rule; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +public class StateMachineGraphReader { + public static StateMachineGraph readFromFile(File file) { + StateMachineGraph smg = null; + try { + FileInputStream fileIn = new FileInputStream(file); + ObjectInputStream in = new ObjectInputStream(fileIn); + smg = (StateMachineGraph) in.readObject(); + System.err.println(smg); + in.close(); + fileIn.close(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + return smg; + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateNode.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateNode.java new file mode 100644 index 00000000..c7245f63 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/StateNode.java @@ -0,0 +1,100 @@ +package de.darmstadt.tu.crossing.rule; + +public class StateNode { + + private final String name; + + private final Boolean init; + private Boolean accepting; + private int hopsToAccepting = Integer.MAX_VALUE; + + public StateNode(String name) { + this(name, false, false); + } + + public StateNode(String name, Boolean init) { + this(name, init, false); + } + + public StateNode(String name, Boolean init, Boolean accepting) { + this.name = name; + this.init = init; + this.accepting = accepting; + } + + public String getName() { + return name; + } + + public Boolean getInit() { + return init; + } + + public Boolean getAccepting() { + return accepting; + } + + public void makeAccepting() { + this.accepting = true; + } + + public void setAccepting(Boolean accepting) { + this.accepting = accepting; + } + + public String toString() { + StringBuilder nodeSB = new StringBuilder(); + nodeSB.append("Name: "); + nodeSB.append(name); + nodeSB.append(" ("); + if (!accepting) { + nodeSB.append(hopsToAccepting + "hops to "); + } + nodeSB.append("accepting)"); + return nodeSB.toString(); + } + + public boolean isErrorState() { + return !accepting; + } + + public boolean isInitialState() { + return init; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((accepting == null) ? 0 : accepting.hashCode()); + result = prime * result + ((init == null) ? 0 : init.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + StateNode other = (StateNode) obj; + if (accepting == null) { + if (other.accepting != null) return false; + } else if (!accepting.equals(other.accepting)) return false; + if (init == null) { + if (other.init != null) return false; + } else if (!init.equals(other.init)) return false; + if (name == null) { + if (other.name != null) return false; + } else if (!name.equals(other.name)) return false; + return true; + } + + public void setHopsToAccepting(int hops) { + hopsToAccepting = hops; + } + + public int getHopsToAccepting() { + return hopsToAccepting; + } +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/Transition.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/Transition.java new file mode 100644 index 00000000..e95e3e89 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/Transition.java @@ -0,0 +1,11 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Collection; + +public interface Transition { + State from(); + + State to(); + + Collection getLabel(); +} diff --git a/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/TransitionEdge.java b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/TransitionEdge.java new file mode 100644 index 00000000..aad72758 --- /dev/null +++ b/CryslParser/src/main/java/de/darmstadt/tu/crossing/rule/TransitionEdge.java @@ -0,0 +1,74 @@ +package de.darmstadt.tu.crossing.rule; + +import java.util.Collection; + +public class TransitionEdge implements Transition { + + private final StateNode left; + private final StateNode right; + private final Collection methods; + + public TransitionEdge(Collection _methods, StateNode _left, StateNode _right) { + left = _left; + right = _right; + methods = _methods; + } + + public StateNode getLeft() { + return left; + } + + public StateNode getRight() { + return right; + } + + public Collection getLabel() { + return methods; + } + + @Override + public String toString() { + return "Left: " + + this.left.getName() + + " ====" + + methods + + "====> Right:" + + this.right.getName(); + } + + public StateNode from() { + return left; + } + + public StateNode to() { + return right; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((methods == null) ? 0 : methods.hashCode()); + result = prime * result + ((left == null) ? 0 : left.hashCode()); + result = prime * result + ((right == null) ? 0 : right.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + TransitionEdge other = (TransitionEdge) obj; + if (methods == null) { + if (other.methods != null) return false; + } else if (!methods.equals(other.methods)) return false; + if (left == null) { + if (other.left != null) return false; + } else if (!left.equals(other.left)) return false; + if (right == null) { + if (other.right != null) return false; + } else if (!right.equals(other.right)) return false; + return true; + } +} diff --git a/CryslParser/src/main/resources/plugin.properties b/CryslParser/src/main/resources/plugin.properties new file mode 100644 index 00000000..5377da11 --- /dev/null +++ b/CryslParser/src/main/resources/plugin.properties @@ -0,0 +1,2 @@ +# Required by emf.core +_UI_DiagnosticRoot_diagnostic=_UI_DiagnosticRoot_diagnostic diff --git a/CryslParser/src/test/java/parser/CrySLParserTest.java b/CryslParser/src/test/java/parser/CrySLParserTest.java new file mode 100644 index 00000000..28a231f7 --- /dev/null +++ b/CryslParser/src/test/java/parser/CrySLParserTest.java @@ -0,0 +1,65 @@ +package parser; + +import de.darmstadt.tu.crossing.CrySLParser; +import de.darmstadt.tu.crossing.rule.CrySLRule; +import java.io.IOException; +import java.util.Collection; +import org.junit.Assert; +import org.junit.Test; + +public class CrySLParserTest { + + private static final String emptyZipFilePath = "src/test/resources/parser/empty.zip"; + private static final String jcaRulesetZipFilePath = + "src/test/resources/parser/JavaCryptographicArchitecture-3.0.1-ruleset.zip"; + private static final String junkRuleSet = "src/test/resources/parser/rulesetWithJunk.zip"; + + @Test + public void testNumberOfRules() throws IOException { + CrySLParser parser = new CrySLParser(); + Collection rules = parser.parseRulesFromPath(jcaRulesetZipFilePath); + + Assert.assertEquals(49, rules.size()); + } + + @Test + public void testRulesZipFile() throws IOException { + CrySLParser parser = new CrySLParser(); + Collection rules = parser.parseRulesFromZipArchive(jcaRulesetZipFilePath); + + Assert.assertEquals(49, rules.size()); + } + + @Test + public void testJunkThrows() throws IOException { + CrySLParser parser = new CrySLParser(); + Collection rules = parser.parseRulesFromPath(junkRuleSet); + + Assert.assertEquals(48, rules.size()); + } + + @Test + public void testFileNoCrySLFiles() throws IOException { + CrySLParser parser = new CrySLParser(); + Collection rules = parser.parseRulesFromPath(emptyZipFilePath); + + Assert.assertEquals(0, rules.size()); + } + + @Test(expected = IOException.class) + public void testFileNotExists() throws IOException { + CrySLParser parser = new CrySLParser(); + Collection rules = parser.parseRulesFromPath("notExist"); + Assert.assertEquals(0, rules.size()); + } + + @Test + public void testRunTwiceSameResult() throws IOException { + CrySLParser parser = new CrySLParser(); + Collection rules = parser.parseRulesFromPath(jcaRulesetZipFilePath); + Assert.assertEquals(49, rules.size()); + + rules = parser.parseRulesFromPath(jcaRulesetZipFilePath); + Assert.assertEquals(49, rules.size()); + } +} diff --git a/CryslParser/src/test/java/statemachine/StateMachineTest.java b/CryslParser/src/test/java/statemachine/StateMachineTest.java new file mode 100644 index 00000000..8a4960d5 --- /dev/null +++ b/CryslParser/src/test/java/statemachine/StateMachineTest.java @@ -0,0 +1,76 @@ +package statemachine; + +import de.darmstadt.tu.crossing.CrySLParser; +import de.darmstadt.tu.crossing.parsing.CrySLException; +import de.darmstadt.tu.crossing.rule.CrySLRule; +import de.darmstadt.tu.crossing.rule.StateMachineGraph; +import de.darmstadt.tu.crossing.rule.TransitionEdge; +import java.io.File; +import java.util.Collection; +import org.junit.Assert; +import org.junit.Test; + +public class StateMachineTest { + + private static final String TEST_PATH = + "." + + File.separator + + "src" + + File.separator + + "test" + + File.separator + + "resources" + + File.separator + + "stateMachineRules" + + File.separator; + private static final String CRYSL_FILE = "StateMachineTester.crysl"; + + @Test + public void testOptionalAfterStar() { + // (Op1?, Op2, Op3)* + StateMachineGraph smg = getStateMachineGraph("optionalAfterStar"); + Collection edges = smg.getEdges(); + + Assert.assertEquals(edges.size(), 7); + Assert.assertTrue(stateMachineContainsEdge("0", "1", edges)); + Assert.assertTrue(stateMachineContainsEdge("0", "2", edges)); + Assert.assertTrue(stateMachineContainsEdge("3", "1", edges)); + Assert.assertTrue(stateMachineContainsEdge("3", "2", edges)); + } + + @Test + public void testOptionalBetweenStar() { + // (Op1, Op2?, Op3)* + StateMachineGraph smg = getStateMachineGraph("optionalBetweenStar"); + Collection edges = smg.getEdges(); + + Assert.assertEquals(edges.size(), 6); + Assert.assertTrue(stateMachineContainsEdge("1", "2", edges)); + Assert.assertTrue(stateMachineContainsEdge("1", "3", edges)); + Assert.assertTrue(stateMachineContainsEdge("3", "1", edges)); + Assert.assertFalse(stateMachineContainsEdge("3", "2", edges)); + } + + private StateMachineGraph getStateMachineGraph(String ruleName) { + try { + CrySLParser parser = new CrySLParser(); + + CrySLRule rule = + parser.parseRuleFromFile( + new File(TEST_PATH + ruleName + File.separator + CRYSL_FILE)); + return rule.getUsagePattern(); + } catch (CrySLException e) { + throw new RuntimeException("Unable to find rules for " + ruleName, e); + } + } + + private boolean stateMachineContainsEdge( + String from, String to, Collection edges) { + for (TransitionEdge edge : edges) { + if (edge.from().getName().equals(from) && edge.to().getName().equals(to)) { + return true; + } + } + return false; + } +} diff --git a/CryslParser/src/test/java/statemachine/StateMachineTester.java b/CryslParser/src/test/java/statemachine/StateMachineTester.java new file mode 100644 index 00000000..aeec9c06 --- /dev/null +++ b/CryslParser/src/test/java/statemachine/StateMachineTester.java @@ -0,0 +1,12 @@ +package statemachine; + +public class StateMachineTester { + + public void operation1() {} + + public void operation2() {} + + public void operation3() {} + + public void operation4() {} +} diff --git a/CryslParser/src/test/resources/parser/JavaCryptographicArchitecture-3.0.1-ruleset.zip b/CryslParser/src/test/resources/parser/JavaCryptographicArchitecture-3.0.1-ruleset.zip new file mode 100644 index 0000000000000000000000000000000000000000..a9f61457b2804d216981c653c2d74ff6acadd26e GIT binary patch literal 22208 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 literal 0 HcmV?d00001 diff --git a/CryslParser/src/test/resources/parser/empty.zip b/CryslParser/src/test/resources/parser/empty.zip new file mode 100644 index 0000000000000000000000000000000000000000..15cb0ecb3e219d1701294bfdf0fe3f5cb5d208e7 GIT binary patch literal 22 NcmWIWW@Tf*000g10H*)| literal 0 HcmV?d00001 diff --git a/CryslParser/src/test/resources/parser/rulesetWithJunk.zip b/CryslParser/src/test/resources/parser/rulesetWithJunk.zip new file mode 100644 index 0000000000000000000000000000000000000000..8bfa1253380b11de8d53a6cd52385411b3319bc1 GIT binary patch literal 27641 zcmbTe1yEk=wk?Rey99!}Yp?`&clRH6C%C(7aCdiicMlNU3GO6V|2f@t@7epE^OD_E zeQ4Dx));e+scXqgfkVK7{Q8&skKqMw2hIo8$1Xo#1S|M2!t{43iAJRU2OkO`9R|RCRE^W ze;s<@`M+P6iHS+l*~U`T+zP_coi5Bfg);^pT5FW z2cRGzgODH~oWK2_1*}YM9n77~tmO?H46Ff800&0~BL{a!tAE|hYB;`8=Kpy!g~|%H zt4t`~P+@O&J7E$qVKcHSsO5?^$)})oEoX%CDy_W*RpR_%oTaNjH(i_Xg-AH(l?eq~ zp1LkRZf549e(7g@8Wbg$^TrbFihR~-8%`;vGlU#(*%Mhgxo;~!u{HJ<@>q`!zEIDK zE};|70xQRvr?DIy1DAUjj?r%?-@TBY$JZs^)x}xs09VEG%`1-C9qhmz4Yz|u{Cq_5 zNoqJH)&cjR(Ad2XdTE1lP^QzhPRMP>w_3YzA7PI9*>7Qfj=(RT1lvKM&$8rFw!bh; z#-dh|eIPa-Q&QFKJ9F=cE$I4pcW37MMF{b{E4Gg&IV)=(oVIO>Ts{$HlG-Xe2O#Bc z0)_!N{&g&Yq>qxs2j%3QVJ|ZcGaQgHVBJq2b6<#Wu?^cXdAHhih8zfB7995z#J$A6 z?m;{FN0PH$@?A_!bB=i8cwncBF3O@iytYD_7f@oGS|8ukK1_oxfhRktH{4|&8+j$T zV0`#2AFH;0lIq!wXe78P>Ou9v25fVSTwRUU;G4%MXU-=~6TKY6U{}j_7Y2VzI(9#i z8HSmUEis6djppZIZ=7smeg|8tAug-b5KYaJ7oVf@NSvc_c{S#=8X==FW99OiKzf=U zeM&C&8bBMdH~cfEmJ%o+Vb-0?m=n2js$7V%403^QY0#c=K{`lsa_jMOO z_E)naAQqKq)6yv6Z}=}$2qZ^?D%5?sOake|i$;a-IYBD1%1c$PH?t(`MjD<7Hl0MY zVpQU*yWXL!Z)v-*zC&i@v+b4WtxYU2@8FE*A3Ta9}St zrhnezsfq%7-)?ES&uG6@v6j_v*aAvYRjd7_0DJfA0E`!lnIb7 zYg*T=I9f|-5S@uAKk}qecMzFzj97nQ(Y|E1h_tSt?3=ttJ!wwyojBk+C;XJQlK!FJ zS#^@Q5LsxP#5u~bqH5*{9e&(!CocY zkYzY!nt(ue1f%`HBFyuil3&2V6#YaZ-lck-e{#7}$v8oOuL}GWuON=`3pmY~Y8eX4 zXNVbc*ctQpEA+SFRzYK%jwSlT&iwk&#_Jv&sGAY55+;7t+ynnjT_s3?0CQ#eKK zxb-H!`4+@nQqi=5`@WQd`cOE0kzx;9%+S0CRF`J`yMd^rcn;i53qixEywQH*gJ8_@ zm*F4?uSlicj?TV6scxmek3;E6D~>5Y9~ zN>?z4FtbB=TK$(St`yMNpYdZwu^B& za3q1%cqvjBe^dNEI(nL0R(={b!hVW6HhK!m;Q>vLGf9MM$%STkBk;O5`9({c zYDwXCuj;k@7E8)O+=E$|pe$!cvC|}NNAAuA?}gd699!rSf4*DNnJ36d>K&Qi`%T

5lIa8#q7?M8p%rpOtxf|PRG<+?YXX`7Z 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 YZN2`X<~xWs8978SR2nO|ZlnC}e^es6C;$Ke literal 0 HcmV?d00001 diff --git a/CryslParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl b/CryslParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl new file mode 100644 index 00000000..e6870274 --- /dev/null +++ b/CryslParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl @@ -0,0 +1,11 @@ +SPEC statemachine.StateMachineTester + +EVENTS + Con: StateMachineTester(); + Op1: operation1(); + Op2: operation2(); + Op3: operation3(); + Op4: operation4(); + +ORDER + Con, (Op1?, Op2, Op3)* diff --git a/CryslParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl b/CryslParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl new file mode 100644 index 00000000..74a1483a --- /dev/null +++ b/CryslParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl @@ -0,0 +1,11 @@ +SPEC statemachine.StateMachineTester + +EVENTS + Con: StateMachineTester(); + Op1: operation1(); + Op2: operation2(); + Op3: operation3(); + Op4: operation4(); + +ORDER + Con, (Op1, Op2?, Op3)* diff --git a/de.darmstadt.tu.crossing.CrySL/pom.xml b/de.darmstadt.tu.crossing.CrySL/pom.xml index 80fae9b5..cc413515 100644 --- a/de.darmstadt.tu.crossing.CrySL/pom.xml +++ b/de.darmstadt.tu.crossing.CrySL/pom.xml @@ -33,7 +33,6 @@ - diff --git a/pom.xml b/pom.xml index 1a76fa51..f94d3476 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,7 @@ + CrySLParser de.darmstadt.tu.crossing.CrySL de.darmstadt.tu.crossing.CrySL.ide de.darmstadt.tu.crossing.CrySL.ui @@ -55,12 +56,10 @@ 2.35.0 2.19.0 UTF-8 - 1.8 - 1.8 + 11 - - - org.eclipse.xtend - xtend-maven-plugin - ${xtextVersion} - - xtend-gen - - - - - compile - xtend-install-debug-info - testCompile - xtend-test-install-debug-info - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.apache.maven.plugins - maven-resources-plugin - [2.4.3,) - - resources - testResources - - - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - [1.9.1,) - - add-resource - add-source - add-test-resource - add-test-source - - - - - - - - - org.eclipse.tycho - tycho-compiler-plugin - [0.23.1,) - - compile - - - - - - - - - org.eclipse.tycho - tycho-packaging-plugin - [0.23.1,) - - build - build-aggregator - validate-id - validate-version - - - - - - - - - - - - org.eclipse.jdt - org.eclipse.jdt.core - 3.39.0 - - - org.eclipse.platform - org.eclipse.equinox.common - - - org.eclipse.platform - org.eclipse.core.runtime - - - - - org.eclipse.jdt - org.eclipse.jdt.compiler.apt - 1.4.300 - - - org.eclipse.platform - org.eclipse.equinox.common - - - org.eclipse.platform - org.eclipse.core.runtime - - - - - org.eclipse.jdt - org.eclipse.jdt.compiler.tool - 1.3.200 - - - org.eclipse.platform - org.eclipse.equinox.common - - - org.eclipse.platform - org.eclipse.core.runtime - - - - - org.eclipse.emf - org.eclipse.emf.codegen - 2.24.0 - - - - - - org.eclipse.tycho - tycho-compiler-plugin - ${tycho-version} - - -err:-forbidden - - - - maven-assembly-plugin - - - jar-with-dependencies - - build - - - - make-assembly - - - single - - - package - - - - - org.apache.maven.plugins - maven-dependency-plugin - 3.5.0 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.13.0 - - ${java.version} - ${java.version} - - - - org.eclipse.tycho - tycho-maven-plugin - ${tycho-version} - true - - - org.eclipse.tycho - tycho-versions-plugin - ${tycho-version} - - - org.eclipse.tycho - target-platform-configuration - ${tycho-version} - - - - de.darmstadt.tu.crossing.CrySL - de.darmstadt.tu.crossing.CrySL.target - ${project.version} - - - - - macosx - cocoa - x86_64 - - - win32 - win32 - x86_64 - - - linux - gtk - x86_64 - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.1 - - - attach-source - - jar - - - - - - org.apache.maven.plugins - maven-release-plugin - 3.1.1 - - @{project.version} - - - - - org.codehaus.mojo - flatten-maven-plugin - 1.6.0 - - true - ossrh - - - - flatten - - flatten - - process-resources - - - flatten.clean - - clean - - clean - - - - - com.diffplug.spotless - spotless-maven-plugin - 2.43.0 - - - - pom.xml - de.darmstadt.tu.crossing.CrySL.feature/pom.xml - de.darmstadt.tu.crossing.CrySL.ide/pom.xml - de.darmstadt.tu.crossing.CrySL.repository/pom.xml - de.darmstadt.tu.crossing.CrySL.tests/pom.xml - de.darmstadt.tu.crossing.CrySL.ui/pom.xml - de.darmstadt.tu.crossing.CrySL/pom.xml - CrySLParser/pom.xml - - - UTF-8 - true - 4 - false - true - false - - - - - 1.24.0 - - - - - - - - - - - - - apply - - - - - - - - - - - deployment - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.7.0 - true - - ossrh - https://s01.oss.sonatype.org - true - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.2.7 - - - sign-artifacts - - sign - - verify - - - --pinentry-mode - loopback - - - - - - - - - - + + + 4.0.0 + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.parent + 3.0.2 + pom + + CrySL-Parent + CrySL domain-specific language + https://github.com/CROSSINGTUD/CryptSL + + + Eclipse Public License - v2.0 + https://www.eclipse.org/legal/epl-2.0/ + + + + + CogniCrypt + CogniCrypt + cognicrypt@eim.upb.de + + + + + CrySLParser + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.ide + de.darmstadt.tu.crossing.CrySL.ui + de.darmstadt.tu.crossing.CrySL.target + de.darmstadt.tu.crossing.CrySL.feature + de.darmstadt.tu.crossing.CrySL.repository + de.darmstadt.tu.crossing.CrySL.tests + + + scm:git:git@github.com:CROSSINGTUD/CryptSL.git + scm:git:ssh://github.com:CROSSINGTUD/CryptSL.git + https://github.com/CROSSINGTUD/CryptSL + + + + + ossrh + https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ + + + ossrh + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + + + + 2.7.5 + 2.35.0 + 2.19.0 + UTF-8 + 11 + + + + + + + + org.eclipse.xtend + xtend-maven-plugin + ${xtextVersion} + + xtend-gen + + + + + compile + xtend-install-debug-info + testCompile + xtend-test-install-debug-info + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-resources-plugin + [2.4.3,) + + resources + testResources + + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + [1.9.1,) + + add-resource + add-source + add-test-resource + add-test-source + + + + + + + + + org.eclipse.tycho + tycho-compiler-plugin + [0.23.1,) + + compile + + + + + + + + + org.eclipse.tycho + tycho-packaging-plugin + [0.23.1,) + + build + build-aggregator + validate-id + validate-version + + + + + + + + + + + + org.eclipse.jdt + org.eclipse.jdt.core + 3.39.0 + + + org.eclipse.platform + org.eclipse.equinox.common + + + org.eclipse.platform + org.eclipse.core.runtime + + + + + org.eclipse.jdt + org.eclipse.jdt.compiler.apt + 1.4.300 + + + org.eclipse.platform + org.eclipse.equinox.common + + + org.eclipse.platform + org.eclipse.core.runtime + + + + + org.eclipse.jdt + org.eclipse.jdt.compiler.tool + 1.3.200 + + + org.eclipse.platform + org.eclipse.equinox.common + + + org.eclipse.platform + org.eclipse.core.runtime + + + + + org.eclipse.emf + org.eclipse.emf.codegen + 2.24.0 + + + + + + org.eclipse.tycho + tycho-compiler-plugin + ${tycho-version} + + -err:-forbidden + + + + maven-assembly-plugin + + + jar-with-dependencies + + build + + + + make-assembly + + + single + + + package + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.5.0 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${java.version} + ${java.version} + + + + org.eclipse.tycho + tycho-maven-plugin + ${tycho-version} + true + + + org.eclipse.tycho + tycho-versions-plugin + ${tycho-version} + + + org.eclipse.tycho + target-platform-configuration + ${tycho-version} + + + + de.darmstadt.tu.crossing.CrySL + de.darmstadt.tu.crossing.CrySL.target + ${project.version} + + + + + macosx + cocoa + x86_64 + + + win32 + win32 + x86_64 + + + linux + gtk + x86_64 + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.1 + + + attach-source + + jar + + + + + + org.apache.maven.plugins + maven-release-plugin + 3.1.1 + + @{project.version} + + + + + org.codehaus.mojo + flatten-maven-plugin + 1.6.0 + + true + ossrh + + + + flatten + + flatten + + process-resources + + + flatten.clean + + clean + + clean + + + + + com.diffplug.spotless + spotless-maven-plugin + 2.43.0 + + + + pom.xml + de.darmstadt.tu.crossing.CrySL.feature/pom.xml + de.darmstadt.tu.crossing.CrySL.ide/pom.xml + de.darmstadt.tu.crossing.CrySL.repository/pom.xml + de.darmstadt.tu.crossing.CrySL.tests/pom.xml + de.darmstadt.tu.crossing.CrySL.ui/pom.xml + de.darmstadt.tu.crossing.CrySL/pom.xml + CrySLParser/pom.xml + + + UTF-8 + true + 4 + false + true + false + + + + + 1.24.0 + + + + + + + + + + + + + apply + + + + + + + + + + + deployment + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.7.0 + true + + ossrh + https://s01.oss.sonatype.org + true + + + + org.apache.maven.plugins + maven-gpg-plugin + 3.2.7 + + + sign-artifacts + + sign + + verify + + + --pinentry-mode + loopback + + + + + + + + + + From f57765ca03cb6ed040ee7fc33025a0feca374795 Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Wed, 27 Nov 2024 09:36:35 +0100 Subject: [PATCH 06/10] Use more suitable naming for packages --- .../tu/crossing => crysl}/CrySLParser.java | 22 ++++---- .../parsing/CrySLModelReader.java | 56 +++++++++---------- .../parsing/CrySLModelReaderClassPath.java | 2 +- .../crysl/parsing/CrySLParserException.java | 8 +++ .../parsing/CrySLReaderUtils.java | 16 +++--- .../parsing/ExceptionsReader.java | 6 +- .../parsing/StateMachineGraphBuilder.java | 10 ++-- .../rule/CrySLArithmeticConstraint.java | 2 +- .../rule/CrySLComparisonConstraint.java | 2 +- .../rule/CrySLCondPredicate.java | 2 +- .../rule/CrySLConstraint.java | 2 +- .../rule/CrySLException.java | 2 +- .../rule/CrySLExceptionConstraint.java | 2 +- .../rule/CrySLForbiddenMethod.java | 2 +- .../crossing => crysl}/rule/CrySLLiteral.java | 2 +- .../crossing => crysl}/rule/CrySLMethod.java | 2 +- .../crossing => crysl}/rule/CrySLObject.java | 2 +- .../rule/CrySLPredicate.java | 2 +- .../tu/crossing => crysl}/rule/CrySLRule.java | 2 +- .../rule/CrySLSplitter.java | 2 +- .../rule/CrySLValueConstraint.java | 2 +- .../rule/FiniteStateMachine.java | 2 +- .../rule/ICrySLPredicateParameter.java | 2 +- .../rule/ISLConstraint.java | 2 +- .../rule/StateMachineGraph.java | 2 +- .../rule/StateMachineGraphReader.java | 2 +- .../tu/crossing => crysl}/rule/StateNode.java | 2 +- .../crossing => crysl}/rule/Transition.java | 2 +- .../rule/TransitionEdge.java | 2 +- .../tu/crossing/parsing/CrySLException.java | 8 --- .../src/test/java/parser/CrySLParserTest.java | 4 +- .../statemachine/StateMachineTest.java | 20 ++++--- .../statemachine/StateMachineTester.java | 2 +- .../StateMachineTester.crysl | 2 +- .../StateMachineTester.crysl | 2 +- 35 files changed, 104 insertions(+), 98 deletions(-) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/CrySLParser.java (92%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/parsing/CrySLModelReader.java (94%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/parsing/CrySLModelReaderClassPath.java (99%) create mode 100644 CrySLParser/src/main/java/crysl/parsing/CrySLParserException.java rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/parsing/CrySLReaderUtils.java (95%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/parsing/ExceptionsReader.java (89%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/parsing/StateMachineGraphBuilder.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLArithmeticConstraint.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLComparisonConstraint.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLCondPredicate.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLConstraint.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLException.java (94%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLExceptionConstraint.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLForbiddenMethod.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLLiteral.java (71%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLMethod.java (98%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLObject.java (97%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLPredicate.java (99%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLRule.java (99%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLSplitter.java (94%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/CrySLValueConstraint.java (96%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/FiniteStateMachine.java (85%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/ICrySLPredicateParameter.java (64%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/ISLConstraint.java (77%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/StateMachineGraph.java (99%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/StateMachineGraphReader.java (94%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/StateNode.java (98%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/Transition.java (78%) rename CrySLParser/src/main/java/{de/darmstadt/tu/crossing => crysl}/rule/TransitionEdge.java (98%) delete mode 100644 CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLException.java rename CrySLParser/src/test/java/{ => rules}/statemachine/StateMachineTest.java (78%) rename CrySLParser/src/test/java/{ => rules}/statemachine/StateMachineTester.java (85%) diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/CrySLParser.java b/CrySLParser/src/main/java/crysl/CrySLParser.java similarity index 92% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/CrySLParser.java rename to CrySLParser/src/main/java/crysl/CrySLParser.java index e7cf8f36..78e50559 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/CrySLParser.java +++ b/CrySLParser/src/main/java/crysl/CrySLParser.java @@ -1,8 +1,8 @@ -package de.darmstadt.tu.crossing; +package crysl; -import de.darmstadt.tu.crossing.parsing.CrySLException; -import de.darmstadt.tu.crossing.parsing.CrySLModelReader; -import de.darmstadt.tu.crossing.rule.CrySLRule; +import crysl.parsing.CrySLParserException; +import crysl.parsing.CrySLModelReader; +import crysl.rule.CrySLRule; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -67,17 +67,17 @@ public Collection parseRulesFromFiles(Collection files) { } result.add(rule); - } catch (CrySLException e) { + } catch (CrySLParserException e) { LOGGER.error(e.getMessage()); } } return result; } - public CrySLRule parseRuleFromFile(File file) throws CrySLException { + public CrySLRule parseRuleFromFile(File file) throws CrySLParserException { String fileName = file.getName(); if (!fileName.endsWith(CRYSL_FILE_ENDING)) { - throw new CrySLException( + throw new CrySLParserException( "The extension of " + fileName + " does not match " + CRYSL_FILE_ENDING); } @@ -119,7 +119,7 @@ public Collection parseRulesFromZipArchive(String path) throws IOExce try { CrySLRule rule = parseRuleFromZipEntry(entry, zipFile, file); result.add(rule); - } catch (CrySLException e) { + } catch (CrySLParserException e) { LOGGER.error(e.getMessage()); } } @@ -128,10 +128,10 @@ public Collection parseRulesFromZipArchive(String path) throws IOExce } private CrySLRule parseRuleFromZipEntry(ZipEntry entry, ZipFile zipFile, File file) - throws CrySLException { + throws CrySLParserException { String entryName = entry.getName(); if (entry.isDirectory() || !entryName.endsWith(CRYSL_FILE_ENDING)) { - throw new CrySLException("ZIP entry " + entryName + " is not a CrySL file"); + throw new CrySLParserException("ZIP entry " + entryName + " is not a CrySL file"); } try { @@ -144,7 +144,7 @@ private CrySLRule parseRuleFromZipEntry(ZipEntry entry, ZipFile zipFile, File fi return rule; } catch (IOException ex) { - throw new CrySLException( + throw new CrySLParserException( "Could not read file " + entry.getName() + " from Zip archive " diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReader.java b/CrySLParser/src/main/java/crysl/parsing/CrySLModelReader.java similarity index 94% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReader.java rename to CrySLParser/src/main/java/crysl/parsing/CrySLModelReader.java index 6472d4ca..f8e8282f 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReader.java +++ b/CrySLParser/src/main/java/crysl/parsing/CrySLModelReader.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.parsing; +package crysl.parsing; import com.google.inject.Injector; import de.darmstadt.tu.crossing.CrySLStandaloneSetup; @@ -32,22 +32,22 @@ import de.darmstadt.tu.crossing.crySL.ThisPredicateParameter; import de.darmstadt.tu.crossing.crySL.TimedPredicate; import de.darmstadt.tu.crossing.crySL.WildcardPredicateParameter; -import de.darmstadt.tu.crossing.rule.CrySLArithmeticConstraint; -import de.darmstadt.tu.crossing.rule.CrySLComparisonConstraint; -import de.darmstadt.tu.crossing.rule.CrySLCondPredicate; -import de.darmstadt.tu.crossing.rule.CrySLConstraint; -import de.darmstadt.tu.crossing.rule.CrySLForbiddenMethod; -import de.darmstadt.tu.crossing.rule.CrySLMethod; -import de.darmstadt.tu.crossing.rule.CrySLObject; -import de.darmstadt.tu.crossing.rule.CrySLPredicate; -import de.darmstadt.tu.crossing.rule.CrySLRule; -import de.darmstadt.tu.crossing.rule.CrySLSplitter; -import de.darmstadt.tu.crossing.rule.CrySLValueConstraint; -import de.darmstadt.tu.crossing.rule.ICrySLPredicateParameter; -import de.darmstadt.tu.crossing.rule.ISLConstraint; -import de.darmstadt.tu.crossing.rule.StateMachineGraph; -import de.darmstadt.tu.crossing.rule.StateNode; -import de.darmstadt.tu.crossing.rule.TransitionEdge; +import crysl.rule.CrySLArithmeticConstraint; +import crysl.rule.CrySLComparisonConstraint; +import crysl.rule.CrySLCondPredicate; +import crysl.rule.CrySLConstraint; +import crysl.rule.CrySLForbiddenMethod; +import crysl.rule.CrySLMethod; +import crysl.rule.CrySLObject; +import crysl.rule.CrySLPredicate; +import crysl.rule.CrySLRule; +import crysl.rule.CrySLSplitter; +import crysl.rule.CrySLValueConstraint; +import crysl.rule.ICrySLPredicateParameter; +import crysl.rule.ISLConstraint; +import crysl.rule.StateMachineGraph; +import crysl.rule.StateNode; +import crysl.rule.TransitionEdge; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -121,12 +121,12 @@ public CrySLModelReader(CrySLModelReaderClassPath classPath) { * @return the {@link CrySLRule} * @throws IllegalArgumentException If the file for the rule cannot be found * @throws IOException If there is a problem with reading the file - * @throws CrySLException If the file is not a .crysl file + * @throws CrySLParserException If the file is not a .crysl file */ public CrySLRule readRule(InputStream stream, String virtualFileName) - throws IOException, CrySLException { + throws IOException, CrySLParserException { if (!virtualFileName.endsWith(cryslFileEnding)) { - throw new CrySLException( + throw new CrySLParserException( "The extension of " + virtualFileName + " does not match " + cryslFileEnding); } @@ -146,9 +146,9 @@ public CrySLRule readRule(InputStream stream, String virtualFileName) * * @param ruleFile the CrySL file * @return the {@link CrySLRule} object - * @throws CrySLException If the file is not a .crysl file + * @throws CrySLParserException If the file is not a .crysl file */ - public CrySLRule readRule(File ruleFile) throws CrySLException { + public CrySLRule readRule(File ruleFile) throws CrySLParserException { Resource resource = resourceSet.getResource(URI.createFileURI(ruleFile.getAbsolutePath()), true); @@ -197,31 +197,31 @@ private boolean runValidator(Resource resource) { return errorFound; } - private CrySLRule createRuleFromResource(Resource resource) throws CrySLException { + private CrySLRule createRuleFromResource(Resource resource) throws CrySLParserException { if (resource == null) { - throw new CrySLException( + throw new CrySLParserException( "Internal error creating a CrySL rule: 'resource parameter was null'."); } if (runValidator(resource)) { - throw new CrySLException( + throw new CrySLParserException( "Skipping rule since it contains errors: " + resource.getURI()); } try { return createRuleFromDomainModel((Domainmodel) resource.getContents().get(0)); } catch (Exception e) { - throw new CrySLException( + throw new CrySLParserException( "An error occurred while reading the rule " + resource.getURI()); } } - private CrySLRule createRuleFromDomainModel(Domainmodel model) throws CrySLException { + private CrySLRule createRuleFromDomainModel(Domainmodel model) throws CrySLParserException { this.currentClass = model.getJavaType(); String currentClass = this.currentClass.getQualifiedName(); if (currentClass.equals("void")) { - throw new CrySLException("Class for the rule is not on the classpath."); + throw new CrySLParserException("Class for the rule is not on the classpath."); } final Collection> objects = getObjects(model.getObjects()); diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReaderClassPath.java b/CrySLParser/src/main/java/crysl/parsing/CrySLModelReaderClassPath.java similarity index 99% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReaderClassPath.java rename to CrySLParser/src/main/java/crysl/parsing/CrySLModelReaderClassPath.java index c3d0e22b..f8c6d8df 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLModelReaderClassPath.java +++ b/CrySLParser/src/main/java/crysl/parsing/CrySLModelReaderClassPath.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.parsing; +package crysl.parsing; import com.google.common.base.Splitter; import java.io.File; diff --git a/CrySLParser/src/main/java/crysl/parsing/CrySLParserException.java b/CrySLParser/src/main/java/crysl/parsing/CrySLParserException.java new file mode 100644 index 00000000..e39d4a00 --- /dev/null +++ b/CrySLParser/src/main/java/crysl/parsing/CrySLParserException.java @@ -0,0 +1,8 @@ +package crysl.parsing; + +public class CrySLParserException extends Exception { + + public CrySLParserException(String message) { + super(message); + } +} diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLReaderUtils.java b/CrySLParser/src/main/java/crysl/parsing/CrySLReaderUtils.java similarity index 95% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLReaderUtils.java rename to CrySLParser/src/main/java/crysl/parsing/CrySLReaderUtils.java index 9673c3ae..09918b46 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLReaderUtils.java +++ b/CrySLParser/src/main/java/crysl/parsing/CrySLReaderUtils.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.parsing; +package crysl.parsing; import de.darmstadt.tu.crossing.crySL.Aggregate; import de.darmstadt.tu.crossing.crySL.AnyParameterType; @@ -16,13 +16,13 @@ import de.darmstadt.tu.crossing.crySL.Object; import de.darmstadt.tu.crossing.crySL.Operator; import de.darmstadt.tu.crossing.crySL.StringLiteral; -import de.darmstadt.tu.crossing.rule.CrySLArithmeticConstraint; -import de.darmstadt.tu.crossing.rule.CrySLComparisonConstraint; -import de.darmstadt.tu.crossing.rule.CrySLConstraint; -import de.darmstadt.tu.crossing.rule.CrySLException; -import de.darmstadt.tu.crossing.rule.CrySLMethod; -import de.darmstadt.tu.crossing.rule.CrySLObject; -import de.darmstadt.tu.crossing.rule.ICrySLPredicateParameter; +import crysl.rule.CrySLArithmeticConstraint; +import crysl.rule.CrySLComparisonConstraint; +import crysl.rule.CrySLConstraint; +import crysl.rule.CrySLException; +import crysl.rule.CrySLMethod; +import crysl.rule.CrySLObject; +import crysl.rule.ICrySLPredicateParameter; import java.util.AbstractMap.SimpleEntry; import java.util.Collection; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/ExceptionsReader.java b/CrySLParser/src/main/java/crysl/parsing/ExceptionsReader.java similarity index 89% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/ExceptionsReader.java rename to CrySLParser/src/main/java/crysl/parsing/ExceptionsReader.java index e90f01eb..58b6a086 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/ExceptionsReader.java +++ b/CrySLParser/src/main/java/crysl/parsing/ExceptionsReader.java @@ -1,9 +1,9 @@ -package de.darmstadt.tu.crossing.parsing; +package crysl.parsing; import de.darmstadt.tu.crossing.crySL.EventsBlock; import de.darmstadt.tu.crossing.crySL.LabeledMethodCall; -import de.darmstadt.tu.crossing.rule.CrySLExceptionConstraint; -import de.darmstadt.tu.crossing.rule.CrySLMethod; +import crysl.rule.CrySLExceptionConstraint; +import crysl.rule.CrySLMethod; import java.util.Collection; import java.util.stream.Collectors; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/StateMachineGraphBuilder.java b/CrySLParser/src/main/java/crysl/parsing/StateMachineGraphBuilder.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/StateMachineGraphBuilder.java rename to CrySLParser/src/main/java/crysl/parsing/StateMachineGraphBuilder.java index 4d145d59..7cf2178e 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/StateMachineGraphBuilder.java +++ b/CrySLParser/src/main/java/crysl/parsing/StateMachineGraphBuilder.java @@ -1,13 +1,13 @@ -package de.darmstadt.tu.crossing.parsing; +package crysl.parsing; import de.darmstadt.tu.crossing.crySL.Event; import de.darmstadt.tu.crossing.crySL.Order; import de.darmstadt.tu.crossing.crySL.OrderOperator; import de.darmstadt.tu.crossing.crySL.Primary; -import de.darmstadt.tu.crossing.rule.CrySLMethod; -import de.darmstadt.tu.crossing.rule.FiniteStateMachine; -import de.darmstadt.tu.crossing.rule.StateMachineGraph; -import de.darmstadt.tu.crossing.rule.StateNode; +import crysl.rule.CrySLMethod; +import crysl.rule.FiniteStateMachine; +import crysl.rule.StateMachineGraph; +import crysl.rule.StateNode; import java.util.Collection; import java.util.Collections; import java.util.HashSet; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLArithmeticConstraint.java b/CrySLParser/src/main/java/crysl/rule/CrySLArithmeticConstraint.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLArithmeticConstraint.java rename to CrySLParser/src/main/java/crysl/rule/CrySLArithmeticConstraint.java index ab27d821..5a9fe7ed 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLArithmeticConstraint.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLArithmeticConstraint.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.ArrayList; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLComparisonConstraint.java b/CrySLParser/src/main/java/crysl/rule/CrySLComparisonConstraint.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLComparisonConstraint.java rename to CrySLParser/src/main/java/crysl/rule/CrySLComparisonConstraint.java index 9b550b8d..edbc1e68 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLComparisonConstraint.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLComparisonConstraint.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLCondPredicate.java b/CrySLParser/src/main/java/crysl/rule/CrySLCondPredicate.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLCondPredicate.java rename to CrySLParser/src/main/java/crysl/rule/CrySLCondPredicate.java index d49dfdc1..16c991d5 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLCondPredicate.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLCondPredicate.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Collection; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLConstraint.java b/CrySLParser/src/main/java/crysl/rule/CrySLConstraint.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLConstraint.java rename to CrySLParser/src/main/java/crysl/rule/CrySLConstraint.java index e950257a..0ab0803e 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLConstraint.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLConstraint.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLException.java b/CrySLParser/src/main/java/crysl/rule/CrySLException.java similarity index 94% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLException.java rename to CrySLParser/src/main/java/crysl/rule/CrySLException.java index 29fdd28d..d9a08aa8 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLException.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLException.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; /** Helper Class to store an {@link Exception} as a String. */ public class CrySLException { diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLExceptionConstraint.java b/CrySLParser/src/main/java/crysl/rule/CrySLExceptionConstraint.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLExceptionConstraint.java rename to CrySLParser/src/main/java/crysl/rule/CrySLExceptionConstraint.java index d1114ca5..4e51b85f 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLExceptionConstraint.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLExceptionConstraint.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Collections; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLForbiddenMethod.java b/CrySLParser/src/main/java/crysl/rule/CrySLForbiddenMethod.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLForbiddenMethod.java rename to CrySLParser/src/main/java/crysl/rule/CrySLForbiddenMethod.java index 4ed81032..4e2639ec 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLForbiddenMethod.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLForbiddenMethod.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Collection; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLLiteral.java b/CrySLParser/src/main/java/crysl/rule/CrySLLiteral.java similarity index 71% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLLiteral.java rename to CrySLParser/src/main/java/crysl/rule/CrySLLiteral.java index 4f328d81..4284953c 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLLiteral.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLLiteral.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; public abstract class CrySLLiteral implements ISLConstraint { diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLMethod.java b/CrySLParser/src/main/java/crysl/rule/CrySLMethod.java similarity index 98% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLMethod.java rename to CrySLParser/src/main/java/crysl/rule/CrySLMethod.java index 7efc0850..13c24b02 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLMethod.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLMethod.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.List; import java.util.Map.Entry; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLObject.java b/CrySLParser/src/main/java/crysl/rule/CrySLObject.java similarity index 97% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLObject.java rename to CrySLParser/src/main/java/crysl/rule/CrySLObject.java index dff929dd..e8d9b57f 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLObject.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLObject.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Arrays; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLPredicate.java b/CrySLParser/src/main/java/crysl/rule/CrySLPredicate.java similarity index 99% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLPredicate.java rename to CrySLParser/src/main/java/crysl/rule/CrySLPredicate.java index f0e7fd7f..70ef36fe 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLPredicate.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLPredicate.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.ArrayList; import java.util.Arrays; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLRule.java b/CrySLParser/src/main/java/crysl/rule/CrySLRule.java similarity index 99% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLRule.java rename to CrySLParser/src/main/java/crysl/rule/CrySLRule.java index 9c434634..239ac7a1 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLRule.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLRule.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Collection; import java.util.LinkedList; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLSplitter.java b/CrySLParser/src/main/java/crysl/rule/CrySLSplitter.java similarity index 94% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLSplitter.java rename to CrySLParser/src/main/java/crysl/rule/CrySLSplitter.java index f32a97c4..9cc96630 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLSplitter.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLSplitter.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; public class CrySLSplitter { diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLValueConstraint.java b/CrySLParser/src/main/java/crysl/rule/CrySLValueConstraint.java similarity index 96% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLValueConstraint.java rename to CrySLParser/src/main/java/crysl/rule/CrySLValueConstraint.java index 989f69e8..7959ceac 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/CrySLValueConstraint.java +++ b/CrySLParser/src/main/java/crysl/rule/CrySLValueConstraint.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.ArrayList; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/FiniteStateMachine.java b/CrySLParser/src/main/java/crysl/rule/FiniteStateMachine.java similarity index 85% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/FiniteStateMachine.java rename to CrySLParser/src/main/java/crysl/rule/FiniteStateMachine.java index 2f16e4b2..a516208c 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/FiniteStateMachine.java +++ b/CrySLParser/src/main/java/crysl/rule/FiniteStateMachine.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Collection; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/ICrySLPredicateParameter.java b/CrySLParser/src/main/java/crysl/rule/ICrySLPredicateParameter.java similarity index 64% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/ICrySLPredicateParameter.java rename to CrySLParser/src/main/java/crysl/rule/ICrySLPredicateParameter.java index 6eca9d59..19392f5b 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/ICrySLPredicateParameter.java +++ b/CrySLParser/src/main/java/crysl/rule/ICrySLPredicateParameter.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; public interface ICrySLPredicateParameter { diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/ISLConstraint.java b/CrySLParser/src/main/java/crysl/rule/ISLConstraint.java similarity index 77% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/ISLConstraint.java rename to CrySLParser/src/main/java/crysl/rule/ISLConstraint.java index 14d009b6..f9a14913 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/ISLConstraint.java +++ b/CrySLParser/src/main/java/crysl/rule/ISLConstraint.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.List; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraph.java b/CrySLParser/src/main/java/crysl/rule/StateMachineGraph.java similarity index 99% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraph.java rename to CrySLParser/src/main/java/crysl/rule/StateMachineGraph.java index 83779cb0..db63bfdd 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraph.java +++ b/CrySLParser/src/main/java/crysl/rule/StateMachineGraph.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.ArrayList; import java.util.Collection; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraphReader.java b/CrySLParser/src/main/java/crysl/rule/StateMachineGraphReader.java similarity index 94% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraphReader.java rename to CrySLParser/src/main/java/crysl/rule/StateMachineGraphReader.java index 13bfc1eb..b0c707d2 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateMachineGraphReader.java +++ b/CrySLParser/src/main/java/crysl/rule/StateMachineGraphReader.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.io.File; import java.io.FileInputStream; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateNode.java b/CrySLParser/src/main/java/crysl/rule/StateNode.java similarity index 98% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateNode.java rename to CrySLParser/src/main/java/crysl/rule/StateNode.java index c7245f63..304cbe50 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/StateNode.java +++ b/CrySLParser/src/main/java/crysl/rule/StateNode.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; public class StateNode { diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/Transition.java b/CrySLParser/src/main/java/crysl/rule/Transition.java similarity index 78% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/Transition.java rename to CrySLParser/src/main/java/crysl/rule/Transition.java index e95e3e89..362abc15 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/Transition.java +++ b/CrySLParser/src/main/java/crysl/rule/Transition.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Collection; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/TransitionEdge.java b/CrySLParser/src/main/java/crysl/rule/TransitionEdge.java similarity index 98% rename from CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/TransitionEdge.java rename to CrySLParser/src/main/java/crysl/rule/TransitionEdge.java index aad72758..8791031e 100644 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/rule/TransitionEdge.java +++ b/CrySLParser/src/main/java/crysl/rule/TransitionEdge.java @@ -1,4 +1,4 @@ -package de.darmstadt.tu.crossing.rule; +package crysl.rule; import java.util.Collection; diff --git a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLException.java b/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLException.java deleted file mode 100644 index 39dfcb6d..00000000 --- a/CrySLParser/src/main/java/de/darmstadt/tu/crossing/parsing/CrySLException.java +++ /dev/null @@ -1,8 +0,0 @@ -package de.darmstadt.tu.crossing.parsing; - -public class CrySLException extends Exception { - - public CrySLException(String message) { - super(message); - } -} diff --git a/CrySLParser/src/test/java/parser/CrySLParserTest.java b/CrySLParser/src/test/java/parser/CrySLParserTest.java index 28a231f7..4819a282 100644 --- a/CrySLParser/src/test/java/parser/CrySLParserTest.java +++ b/CrySLParser/src/test/java/parser/CrySLParserTest.java @@ -1,7 +1,7 @@ package parser; -import de.darmstadt.tu.crossing.CrySLParser; -import de.darmstadt.tu.crossing.rule.CrySLRule; +import crysl.CrySLParser; +import crysl.rule.CrySLRule; import java.io.IOException; import java.util.Collection; import org.junit.Assert; diff --git a/CrySLParser/src/test/java/statemachine/StateMachineTest.java b/CrySLParser/src/test/java/rules/statemachine/StateMachineTest.java similarity index 78% rename from CrySLParser/src/test/java/statemachine/StateMachineTest.java rename to CrySLParser/src/test/java/rules/statemachine/StateMachineTest.java index 8a4960d5..8fc36e35 100644 --- a/CrySLParser/src/test/java/statemachine/StateMachineTest.java +++ b/CrySLParser/src/test/java/rules/statemachine/StateMachineTest.java @@ -1,10 +1,10 @@ -package statemachine; +package rules.statemachine; -import de.darmstadt.tu.crossing.CrySLParser; -import de.darmstadt.tu.crossing.parsing.CrySLException; -import de.darmstadt.tu.crossing.rule.CrySLRule; -import de.darmstadt.tu.crossing.rule.StateMachineGraph; -import de.darmstadt.tu.crossing.rule.TransitionEdge; +import crysl.CrySLParser; +import crysl.parsing.CrySLParserException; +import crysl.rule.CrySLRule; +import crysl.rule.StateMachineGraph; +import crysl.rule.TransitionEdge; import java.io.File; import java.util.Collection; import org.junit.Assert; @@ -32,8 +32,11 @@ public void testOptionalAfterStar() { Collection edges = smg.getEdges(); Assert.assertEquals(edges.size(), 7); + Assert.assertTrue(stateMachineContainsEdge("-1", "0", edges)); Assert.assertTrue(stateMachineContainsEdge("0", "1", edges)); Assert.assertTrue(stateMachineContainsEdge("0", "2", edges)); + Assert.assertTrue(stateMachineContainsEdge("1", "2", edges)); + Assert.assertTrue(stateMachineContainsEdge("2", "3", edges)); Assert.assertTrue(stateMachineContainsEdge("3", "1", edges)); Assert.assertTrue(stateMachineContainsEdge("3", "2", edges)); } @@ -45,8 +48,11 @@ public void testOptionalBetweenStar() { Collection edges = smg.getEdges(); Assert.assertEquals(edges.size(), 6); + Assert.assertTrue(stateMachineContainsEdge("-1", "0", edges)); + Assert.assertTrue(stateMachineContainsEdge("0", "1", edges)); Assert.assertTrue(stateMachineContainsEdge("1", "2", edges)); Assert.assertTrue(stateMachineContainsEdge("1", "3", edges)); + Assert.assertTrue(stateMachineContainsEdge("2", "3", edges)); Assert.assertTrue(stateMachineContainsEdge("3", "1", edges)); Assert.assertFalse(stateMachineContainsEdge("3", "2", edges)); } @@ -59,7 +65,7 @@ private StateMachineGraph getStateMachineGraph(String ruleName) { parser.parseRuleFromFile( new File(TEST_PATH + ruleName + File.separator + CRYSL_FILE)); return rule.getUsagePattern(); - } catch (CrySLException e) { + } catch (CrySLParserException e) { throw new RuntimeException("Unable to find rules for " + ruleName, e); } } diff --git a/CrySLParser/src/test/java/statemachine/StateMachineTester.java b/CrySLParser/src/test/java/rules/statemachine/StateMachineTester.java similarity index 85% rename from CrySLParser/src/test/java/statemachine/StateMachineTester.java rename to CrySLParser/src/test/java/rules/statemachine/StateMachineTester.java index aeec9c06..39b69709 100644 --- a/CrySLParser/src/test/java/statemachine/StateMachineTester.java +++ b/CrySLParser/src/test/java/rules/statemachine/StateMachineTester.java @@ -1,4 +1,4 @@ -package statemachine; +package rules.statemachine; public class StateMachineTester { diff --git a/CrySLParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl b/CrySLParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl index e6870274..149763e8 100644 --- a/CrySLParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl +++ b/CrySLParser/src/test/resources/stateMachineRules/optionalAfterStar/StateMachineTester.crysl @@ -1,4 +1,4 @@ -SPEC statemachine.StateMachineTester +SPEC rules.statemachine.StateMachineTester EVENTS Con: StateMachineTester(); diff --git a/CrySLParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl b/CrySLParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl index 74a1483a..1439c844 100644 --- a/CrySLParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl +++ b/CrySLParser/src/test/resources/stateMachineRules/optionalBetweenStar/StateMachineTester.crysl @@ -1,4 +1,4 @@ -SPEC statemachine.StateMachineTester +SPEC rules.statemachine.StateMachineTester EVENTS Con: StateMachineTester(); From 533eaf8d9850598c81407d91fd32cb54cf5ebc1e Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Wed, 27 Nov 2024 09:38:53 +0100 Subject: [PATCH 07/10] Fix style --- .../src/main/java/crysl/CrySLParser.java | 2 +- .../java/crysl/parsing/CrySLModelReader.java | 32 +++++++++---------- .../java/crysl/parsing/CrySLReaderUtils.java | 14 ++++---- .../java/crysl/parsing/ExceptionsReader.java | 4 +-- .../parsing/StateMachineGraphBuilder.java | 8 ++--- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/CrySLParser/src/main/java/crysl/CrySLParser.java b/CrySLParser/src/main/java/crysl/CrySLParser.java index 78e50559..37902e4d 100644 --- a/CrySLParser/src/main/java/crysl/CrySLParser.java +++ b/CrySLParser/src/main/java/crysl/CrySLParser.java @@ -1,7 +1,7 @@ package crysl; -import crysl.parsing.CrySLParserException; import crysl.parsing.CrySLModelReader; +import crysl.parsing.CrySLParserException; import crysl.rule.CrySLRule; import java.io.File; import java.io.FileNotFoundException; diff --git a/CrySLParser/src/main/java/crysl/parsing/CrySLModelReader.java b/CrySLParser/src/main/java/crysl/parsing/CrySLModelReader.java index f8e8282f..3783edc8 100644 --- a/CrySLParser/src/main/java/crysl/parsing/CrySLModelReader.java +++ b/CrySLParser/src/main/java/crysl/parsing/CrySLModelReader.java @@ -1,6 +1,22 @@ package crysl.parsing; import com.google.inject.Injector; +import crysl.rule.CrySLArithmeticConstraint; +import crysl.rule.CrySLComparisonConstraint; +import crysl.rule.CrySLCondPredicate; +import crysl.rule.CrySLConstraint; +import crysl.rule.CrySLForbiddenMethod; +import crysl.rule.CrySLMethod; +import crysl.rule.CrySLObject; +import crysl.rule.CrySLPredicate; +import crysl.rule.CrySLRule; +import crysl.rule.CrySLSplitter; +import crysl.rule.CrySLValueConstraint; +import crysl.rule.ICrySLPredicateParameter; +import crysl.rule.ISLConstraint; +import crysl.rule.StateMachineGraph; +import crysl.rule.StateNode; +import crysl.rule.TransitionEdge; import de.darmstadt.tu.crossing.CrySLStandaloneSetup; import de.darmstadt.tu.crossing.crySL.AlternativeRequiredPredicates; import de.darmstadt.tu.crossing.crySL.BuiltinPredicate; @@ -32,22 +48,6 @@ import de.darmstadt.tu.crossing.crySL.ThisPredicateParameter; import de.darmstadt.tu.crossing.crySL.TimedPredicate; import de.darmstadt.tu.crossing.crySL.WildcardPredicateParameter; -import crysl.rule.CrySLArithmeticConstraint; -import crysl.rule.CrySLComparisonConstraint; -import crysl.rule.CrySLCondPredicate; -import crysl.rule.CrySLConstraint; -import crysl.rule.CrySLForbiddenMethod; -import crysl.rule.CrySLMethod; -import crysl.rule.CrySLObject; -import crysl.rule.CrySLPredicate; -import crysl.rule.CrySLRule; -import crysl.rule.CrySLSplitter; -import crysl.rule.CrySLValueConstraint; -import crysl.rule.ICrySLPredicateParameter; -import crysl.rule.ISLConstraint; -import crysl.rule.StateMachineGraph; -import crysl.rule.StateNode; -import crysl.rule.TransitionEdge; import java.io.File; import java.io.IOException; import java.io.InputStream; diff --git a/CrySLParser/src/main/java/crysl/parsing/CrySLReaderUtils.java b/CrySLParser/src/main/java/crysl/parsing/CrySLReaderUtils.java index 09918b46..59bfb0c0 100644 --- a/CrySLParser/src/main/java/crysl/parsing/CrySLReaderUtils.java +++ b/CrySLParser/src/main/java/crysl/parsing/CrySLReaderUtils.java @@ -1,5 +1,12 @@ package crysl.parsing; +import crysl.rule.CrySLArithmeticConstraint; +import crysl.rule.CrySLComparisonConstraint; +import crysl.rule.CrySLConstraint; +import crysl.rule.CrySLException; +import crysl.rule.CrySLMethod; +import crysl.rule.CrySLObject; +import crysl.rule.ICrySLPredicateParameter; import de.darmstadt.tu.crossing.crySL.Aggregate; import de.darmstadt.tu.crossing.crySL.AnyParameterType; import de.darmstadt.tu.crossing.crySL.BooleanLiteral; @@ -16,13 +23,6 @@ import de.darmstadt.tu.crossing.crySL.Object; import de.darmstadt.tu.crossing.crySL.Operator; import de.darmstadt.tu.crossing.crySL.StringLiteral; -import crysl.rule.CrySLArithmeticConstraint; -import crysl.rule.CrySLComparisonConstraint; -import crysl.rule.CrySLConstraint; -import crysl.rule.CrySLException; -import crysl.rule.CrySLMethod; -import crysl.rule.CrySLObject; -import crysl.rule.ICrySLPredicateParameter; import java.util.AbstractMap.SimpleEntry; import java.util.Collection; import java.util.List; diff --git a/CrySLParser/src/main/java/crysl/parsing/ExceptionsReader.java b/CrySLParser/src/main/java/crysl/parsing/ExceptionsReader.java index 58b6a086..355e28d8 100644 --- a/CrySLParser/src/main/java/crysl/parsing/ExceptionsReader.java +++ b/CrySLParser/src/main/java/crysl/parsing/ExceptionsReader.java @@ -1,9 +1,9 @@ package crysl.parsing; -import de.darmstadt.tu.crossing.crySL.EventsBlock; -import de.darmstadt.tu.crossing.crySL.LabeledMethodCall; import crysl.rule.CrySLExceptionConstraint; import crysl.rule.CrySLMethod; +import de.darmstadt.tu.crossing.crySL.EventsBlock; +import de.darmstadt.tu.crossing.crySL.LabeledMethodCall; import java.util.Collection; import java.util.stream.Collectors; diff --git a/CrySLParser/src/main/java/crysl/parsing/StateMachineGraphBuilder.java b/CrySLParser/src/main/java/crysl/parsing/StateMachineGraphBuilder.java index 7cf2178e..23a14c0d 100644 --- a/CrySLParser/src/main/java/crysl/parsing/StateMachineGraphBuilder.java +++ b/CrySLParser/src/main/java/crysl/parsing/StateMachineGraphBuilder.java @@ -1,13 +1,13 @@ package crysl.parsing; -import de.darmstadt.tu.crossing.crySL.Event; -import de.darmstadt.tu.crossing.crySL.Order; -import de.darmstadt.tu.crossing.crySL.OrderOperator; -import de.darmstadt.tu.crossing.crySL.Primary; import crysl.rule.CrySLMethod; import crysl.rule.FiniteStateMachine; import crysl.rule.StateMachineGraph; import crysl.rule.StateNode; +import de.darmstadt.tu.crossing.crySL.Event; +import de.darmstadt.tu.crossing.crySL.Order; +import de.darmstadt.tu.crossing.crySL.OrderOperator; +import de.darmstadt.tu.crossing.crySL.Primary; import java.util.Collection; import java.util.Collections; import java.util.HashSet; From bf1e76544c9ed17439fb4003b09fa0791f3b3faf Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Wed, 27 Nov 2024 13:28:40 +0100 Subject: [PATCH 08/10] Change version to Java 17 --- .github/dependabot.yml | 4 ---- .github/workflows/deploy.yml | 2 +- .github/workflows/main_build.yml | 2 +- .github/workflows/version.yml | 2 +- CrySLParser/pom.xml | 15 +++++++++++++++ pom.xml | 2 +- 6 files changed, 19 insertions(+), 8 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b262bce6..59bff77c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -6,7 +6,3 @@ updates: interval: weekly time: "04:00" open-pull-requests-limit: 10 - # tycho 3 and tycho 4 requires Java version 17 to be built - ignore: - - dependency-name: "org.eclipse.tycho:tycho-maven-plugin" - versions: ["3.x.x", "4.x.x"] diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a470d022..f1f52f62 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,7 @@ jobs: with: distribution: 'adopt' java-package: 'jdk' - java-version: '11' + java-version: '17' server-id: 'ossrh' # must match the serverId configured for the nexus-staging-maven-plugin server-username: OSSRH_USERNAME # Env var that holds your OSSRH user name server-password: OSSRH_PASSWORD # Env var that holds your OSSRH user pw diff --git a/.github/workflows/main_build.yml b/.github/workflows/main_build.yml index 0524ad40..548d4a54 100644 --- a/.github/workflows/main_build.yml +++ b/.github/workflows/main_build.yml @@ -25,7 +25,7 @@ jobs: with: distribution: 'adopt' java-package: jdk - java-version: '11' + java-version: '17' # Restores Maven dependecies - name: Restore local Maven repository uses: actions/cache@v3 diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 631fc1c1..267a3164 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -24,7 +24,7 @@ jobs: with: distribution: 'adopt' java-package: jdk - java-version: '11' + java-version: '17' # Semantic versioning - name: Semantic versioning id: versioning diff --git a/CrySLParser/pom.xml b/CrySLParser/pom.xml index 814779a8..36f0aaed 100644 --- a/CrySLParser/pom.xml +++ b/CrySLParser/pom.xml @@ -37,6 +37,11 @@ + + org.ow2.asm + asm + 9.7.1 + ${project.groupId} de.darmstadt.tu.crossing.CrySL @@ -67,6 +72,16 @@ org.eclipse.emf.ecore ${emfVersion} + + com.google.guava + guava + 33.3.1-jre + + + com.google.inject + guice + 7.0.0 + javax.servlet javax.servlet-api diff --git a/pom.xml b/pom.xml index 11b438ed..84f8655d 100644 --- a/pom.xml +++ b/pom.xml @@ -56,7 +56,7 @@ 2.35.0 2.19.0 UTF-8 - 11 + 17 From 078892f086055399760259ecbaf7663b154fab00 Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Wed, 27 Nov 2024 14:04:46 +0100 Subject: [PATCH 09/10] Update README.md --- README.md | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb60951f..3267ab9e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,31 @@ -# CryptSL +# CrySL (formerly CryptSL) -CrySL is a domain-specific language that allows to specify the correct usage of cryptographic APIs. +CrySL is a domain-specific language that allows to specify the correct usage of APIs. Example specifications for the JavaCryptographicArchitecture (JCA), BouncyCastle and Tink can be found [here](https://github.com/CROSSINGTUD/Crypto-API-Rules). + +## Parsing CrySL files +We implemented a parser that reads a set of `.crysl` files and parses them into Java objects. Include the following dependency in your project (replace `x.y.z` with the latest version): + +```xml + + de.darmstadt.tu.crossing.CrySL + CrySLParser + x.y.z + +``` + +In your program, you can import the `crysl.CrySLParser` and read a set of `.crysl` files from a directory or a `.zip` file: + +```java +CrySLParser parser = new CrySLParser(); +Collection rules = parser.parseRulesFromPath("path/to/directory_or_zip_file"); + +System.out.println("Found " + rules.size() + " for classes:"); +for (CrySLRule rule : rules) { + System.out.println(rule.getClassName()); +} +``` + +## Publications + Paper: https://ieeexplore.ieee.org/document/8880510 _More information can be found under the following [link](https://www.eclipse.org/cognicrypt/documentation/crysl/)._ From 1a53770e8ccbcb5b5919e503f00772c317c19595 Mon Sep 17 00:00:00 2001 From: Sven Meyer Date: Wed, 27 Nov 2024 14:13:24 +0100 Subject: [PATCH 10/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3267ab9e..f7f6c64e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ In your program, you can import the `crysl.CrySLParser` and read a set of `.crys CrySLParser parser = new CrySLParser(); Collection rules = parser.parseRulesFromPath("path/to/directory_or_zip_file"); -System.out.println("Found " + rules.size() + " for classes:"); +System.out.println("Found " + rules.size() + " rules for classes:"); for (CrySLRule rule : rules) { System.out.println(rule.getClassName()); }