├── .github ├── dependabot.yml ├── release.yml └── workflows │ ├── build-java.yml │ ├── commit-message-check-format.yml │ ├── release-create-R-release-branch.yml │ ├── release-create-gh-release.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── NOTICE ├── R └── bpmnLayoutGeneratoR │ ├── .Rbuildignore │ ├── .gitignore │ ├── DESCRIPTION │ ├── Makefile │ ├── NAMESPACE │ ├── R │ ├── bpmnLayoutGeneratoR.R │ └── init.R │ ├── README.adoc │ ├── bpmnLayoutGeneratoR.Rproj │ └── img │ └── readme_bpmn_display.png ├── README.md ├── docs └── images │ ├── release_01_find_commits_since_previous_release.png │ └── release_02_list_commits_since_previous_release.png └── java ├── .mvn └── wrapper │ └── maven-wrapper.properties ├── Makefile ├── README.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── io │ │ └── process │ │ └── analytics │ │ └── tools │ │ └── bpmn │ │ └── generator │ │ ├── App.java │ │ ├── BPMNLayoutGenerator.java │ │ ├── algo │ │ ├── ShapeLayouter.java │ │ └── ShapeSorter.java │ │ ├── converter │ │ ├── AlgoToDisplayModelConverter.java │ │ ├── BpmnToAlgoModelConverter.java │ │ ├── Configuration.java │ │ └── waypoint │ │ │ ├── BendConfiguration.java │ │ │ ├── BendDirection.java │ │ │ ├── Direction.java │ │ │ ├── EdgeTerminalPoints.java │ │ │ ├── GridSearcher.java │ │ │ ├── Orientation.java │ │ │ ├── WayPointDescriptor.java │ │ │ ├── WayPointsComputer.java │ │ │ ├── WayPointsConverter.java │ │ │ └── WayPointsPositioner.java │ │ ├── export │ │ ├── ASCIIExporter.java │ │ ├── BPMNExporter.java │ │ └── SVGExporter.java │ │ ├── input │ │ └── CSVtoBPMN.java │ │ ├── internal │ │ ├── BPMNDiagramRichBuilder.java │ │ ├── BpmnInOut.java │ │ ├── BpmnNamespacePrefixMapper.java │ │ ├── FileUtils.java │ │ ├── IdUtils.java │ │ ├── Semantic.java │ │ ├── StringUtils.java │ │ └── XmlParser.java │ │ └── model │ │ ├── Diagram.java │ │ ├── Edge.java │ │ ├── Grid.java │ │ ├── Position.java │ │ ├── Shape.java │ │ ├── ShapeType.java │ │ └── display │ │ ├── DisplayDimension.java │ │ ├── DisplayEdge.java │ │ ├── DisplayFlowNode.java │ │ ├── DisplayLabel.java │ │ ├── DisplayModel.java │ │ └── DisplayPoint.java ├── resources │ └── log4j2.xml └── xsd │ ├── BPMN20.xsd │ ├── BPMNDI.xsd │ ├── DC.xsd │ ├── DI.xsd │ └── Semantic.xsd └── test ├── java └── io │ └── process │ └── analytics │ └── tools │ └── bpmn │ └── generator │ ├── AppFromBpmnTest.java │ ├── AppFromCsvTest.java │ ├── AppTest.java │ ├── algo │ ├── ShapeLayouterTest.java │ └── ShapeSorterTest.java │ ├── converter │ ├── BpmnToAlgoModelConverterTest.java │ └── waypoint │ │ └── WayPointsPositionerTest.java │ ├── export │ └── SVGExporterTest.java │ ├── input │ └── CSVtoBPMNTest.java │ ├── internal │ ├── BPMNDiagramRichBuilderTest.java │ ├── SemanticTest.java │ └── XmlParserTest.java │ └── model │ └── GridTest.java └── resources ├── bpmn ├── 01-startEvent.bpmn.xml ├── 02-startEvent_task_endEvent-without-collaboration.bpmn.xml ├── A.2.0.bpmn.xml ├── A.2.1.bpmn.xml ├── waypoints-avoid-edge-overlap-01-single_branch.bpmn.png ├── waypoints-avoid-edge-overlap-01-single_branch.bpmn.xml ├── waypoints-avoid-edge-overlap-02-2nd-branch-with-large-height.bpmn.png ├── waypoints-avoid-edge-overlap-02-2nd-branch-with-large-height.bpmn.xml ├── waypoints-avoid-edge-overlap-03-elements_in_front.bpmn.png ├── waypoints-avoid-edge-overlap-03-elements_in_front.bpmn.xml ├── waypoints-avoid-edge-overlap-04-multiple_empty_paths.bpmn.png ├── waypoints-avoid-edge-overlap-04-multiple_empty_paths.bpmn.xml ├── waypoints-positions-cycle_01_simple.bpmn.png ├── waypoints-positions-cycle_01_simple.bpmn.xml ├── waypoints-positions-cycle_02_gateways_in_cycle.bpmn.png ├── waypoints-positions-cycle_02_gateways_in_cycle.bpmn.xml ├── waypoints-positions-gateways.bpmn.xml ├── waypoints-positions-gateways.png ├── waypoints-positions-gateways_split_join.bpmn.xml └── waypoints-positions-gateways_split_join.png ├── csv ├── PatientsProcess │ ├── edge.csv │ ├── edgeSimple.csv │ ├── gateway_edge_simple.csv │ ├── gateway_node_simple.csv │ ├── gateways_edge.csv │ ├── gateways_node.csv │ ├── node.csv │ └── nodeSimple.csv ├── VacationRequestBonita │ ├── edges.csv │ └── nodes.csv └── VacationRequestBonita_v2 │ ├── edges.csv │ └── nodes.csv └── log4j2.xml /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates 2 | version: 2 3 | updates: 4 | - package-ecosystem: "github-actions" 5 | # Workflow files stored in the default location of `.github/workflows` 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | day: "tuesday" 10 | open-pull-requests-limit: 2 11 | rebase-strategy: "disabled" 12 | commit-message: 13 | prefix: "chore(gha)" 14 | labels: 15 | - dependencies 16 | - github_actions 17 | - skip-changelog 18 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - skip-changelog 5 | categories: 6 | - title: 🎉 New Features 7 | labels: 8 | - enhancement 9 | - title: 🐛 Bug Fixes 10 | labels: 11 | - bug 12 | - title: 📝 Documentation 13 | labels: 14 | - documentation 15 | - title: 📦 Dependency updates 16 | labels: 17 | - dependencies 18 | - title: ⚙️ Other Changes 19 | labels: 20 | - "*" 21 | -------------------------------------------------------------------------------- /.github/workflows/build-java.yml: -------------------------------------------------------------------------------- 1 | name: Build Java 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths: 7 | - .github/workflows/build-java.yml 8 | - java/src/**/* 9 | - java/.mvn/**/* 10 | - java/pom.xml 11 | - java/mvnw 12 | pull_request: 13 | branches: [ master ] 14 | paths: 15 | - .github/workflows/build-java.yml 16 | - java/src/**/* 17 | - java/.mvn/**/* 18 | - java/pom.xml 19 | - java/mvnw 20 | 21 | jobs: 22 | build: 23 | runs-on: ubuntu-24.04 24 | strategy: 25 | # don't cancel running jobs even if one fails 26 | fail-fast: false 27 | matrix: 28 | java: 29 | - 17 30 | - 21 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: actions/setup-java@v4 35 | with: 36 | java-version: ${{ matrix.java }} 37 | distribution: 'zulu' 38 | cache: maven 39 | - name: Build 40 | run: ./mvnw -V verify 41 | working-directory: java 42 | -------------------------------------------------------------------------------- /.github/workflows/commit-message-check-format.yml: -------------------------------------------------------------------------------- 1 | name: Commit Message format check 2 | 3 | on: 4 | pull_request_target: 5 | # trigger when the PR title changes 6 | types: [opened, edited, reopened] 7 | 8 | jobs: 9 | pr-title: 10 | runs-on: ubuntu-24.04 11 | permissions: 12 | pull-requests: write # post comments when the PR title doesn't match the "Conventional Commits" rules 13 | steps: 14 | - name: Check Pull Request title 15 | uses: bonitasoft/actions/packages/pr-title-conventional-commits@v3 16 | -------------------------------------------------------------------------------- /.github/workflows/release-create-R-release-branch.yml: -------------------------------------------------------------------------------- 1 | name: Release - create the R package release branch 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-24.04 11 | defaults: 12 | run: 13 | working-directory: ./R/bpmnLayoutGeneratoR 14 | steps: 15 | - name: Setup checkout 16 | uses: actions/checkout@v4 17 | with: 18 | # Use a PAT to ensure that 19 | # commits are authored with a specific user 20 | # workflow run are triggered after git push 21 | token: ${{ secrets.GH_RELEASE_TOKEN }} 22 | - name: Config git 23 | run: | 24 | git config --local user.email "${{ vars.PA_BOT_EMAIL }}" 25 | git config --local user.name "${{ vars.PA_BOT_NAME }}" 26 | git config pull.rebase true 27 | 28 | - name: Set env 29 | run: | 30 | echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV 31 | 32 | - uses: actions/setup-java@v4 33 | with: 34 | java-version: 17 35 | distribution: 'zulu' 36 | cache: 'maven' 37 | 38 | - name: Create release branch 39 | run: | 40 | git checkout -b bpmnLayoutGeneratoR-${{ env.VERSION }} 41 | 42 | - name: Make the R package 43 | run: | 44 | make install 45 | 46 | - name: Commit and push 47 | run: | 48 | release(R): bpmnLayoutGeneratoR-${{ env.VERSION }} 49 | git push --set-upstream origin release/v${{ env.VERSION }} 50 | -------------------------------------------------------------------------------- /.github/workflows/release-create-gh-release.yml: -------------------------------------------------------------------------------- 1 | name: Release - create the GitHub release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | create_release: 10 | runs-on: ubuntu-24.04 11 | permissions: 12 | contents: write # create the GH release 13 | steps: 14 | - name: Set env 15 | run: | 16 | echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV 17 | - name: Create release 18 | uses: ncipollo/release-action@v1 19 | with: 20 | body: | 21 | **Adapt this one liner summary** 22 | ⚡ This new version improves ... ⚡ 23 | 24 | _If appropriate, briefly explain the contents of the new version._ 25 | 26 | ## Breaking changes 27 | 28 | **TODO: keep if relevant and follow the guidelines below** 29 | 30 | - explain why it is introduced 31 | - explain the impact (use case, usage, impact a lot of user or only few, ....) 32 | - add references to issue or pull request to help users to understand the breaking change 33 | 34 | ### Removal of deprecated API 35 | 36 | **TODO: keep if relevant and follow the guidelines below** 37 | 38 | - list API 39 | - add reference to the version and release notes where it was announced as "deprecated" 40 | 41 | ## Deprecated APIs 42 | 43 | **TODO: keep if relevant and follow the guidelines below** 44 | 45 | - list the APIs 46 | - explain why they are deprecated 47 | - provide the new API to use instead. If none exist, mention it 48 | - explain in which version it will be removed. We usually keep 3 minor versions prior removal. 49 | - ensure that an issue exists to track the removal (one by version is OK) and it is attached to a milestone related to the version 50 | 51 | ## Highlights 52 | 53 | **Add screenshots, animations or videos to make your description more user-friendly!** 54 | 55 | ℹ️ For more details, see #. 56 | 57 | ### Other changes.... adapt and create more paragraphs 58 | draft: true 59 | generateReleaseNotes: true 60 | name: ${{ env.VERSION }} 61 | token: ${{ secrets.GH_RELEASE_TOKEN }} 62 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'The version to release' 8 | required: true 9 | 10 | jobs: 11 | releaseVersion: 12 | runs-on: ubuntu-24.04 13 | env: 14 | MAVEN_ARGS: "-B -ntp" 15 | VERSION: ${{ github.event.inputs.version }} 16 | steps: 17 | - run: | 18 | echo "Version: ${{ env.VERSION }}" 19 | 20 | - name: Setup checkout 21 | uses: actions/checkout@v4 22 | with: 23 | # Use a PAT to ensure that 24 | # commits are authored with a specific user 25 | # workflow run are triggered after git push 26 | token: ${{ secrets.GH_RELEASE_TOKEN }} 27 | - name: Config git 28 | run: | 29 | git config --local user.email "${{ vars.PA_BOT_EMAIL }}" 30 | git config --local user.name "${{ vars.PA_BOT_NAME }}" 31 | git config pull.rebase true 32 | - name: Checkout default branch 33 | run: git checkout master && git pull --tags 34 | 35 | - uses: actions/setup-java@v4 36 | with: 37 | java-version: 17 38 | distribution: 'zulu' 39 | cache: 'maven' 40 | - name: Set release version of the java project 41 | run: | 42 | cd ${{ github.workspace }}/java && ./mvnw ${{env.MAVEN_ARGS}} versions:set -DnewVersion=${{ env.VERSION }} versions:commit 43 | cd ${{ github.workspace }} 44 | git commit -a -m "chore(release): ${{ env.VERSION }}" 45 | 46 | - name: Create the release tag 47 | run: | 48 | git tag -a v${{ env.VERSION }} -m "chore(release): ${{ env.VERSION }}" 49 | - name: Set development version of the java project 50 | run: | 51 | cd ${{ github.workspace }}/java && ./mvnw ${{env.MAVEN_ARGS}} versions:set -DnextSnapshot=true versions:commit 52 | cd ${{ github.workspace }} 53 | git commit -a -m "chore(release): prepare next development version" 54 | 55 | - name: Push commits and tags 56 | run: | 57 | git push 58 | git push --tags 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build 2 | target/ 3 | out/ 4 | 5 | # IDE 6 | .idea/ 7 | *.iml 8 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Bonitasoft S.A. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: bpmnLayoutGeneratoR 2 | Title: What the Package Does (One Line, Title Case) 3 | Version: 0.0.0.9000 4 | Authors@R: 5 | person(given = "First", 6 | family = "Last", 7 | role = c("aut", "cre"), 8 | email = "first.last@example.com", 9 | comment = c(ORCID = "YOUR-ORCID-ID")) 10 | Description: What the package does (one paragraph). 11 | Imports: rJava (>= 1.0.4) 12 | SystemRequirements: Java (>= 8) 13 | License: `use_mit_license()`, `use_gpl3_license()` or friends to pick a 14 | license 15 | Encoding: UTF-8 16 | LazyData: true 17 | Roxygen: list(markdown = TRUE) 18 | RoxygenNote: 7.1.1 19 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE_NAME=rJavaPackageExample 2 | # Path to subproject. If there are more subprojects, more variables like this 3 | # have to be added. 4 | # We require each subproject to contain a Makefile with `build` and `clean` 5 | # targets defined: `build` builds subprojects' artefacts, and `clean` removes 6 | # files derived from source code. We also require building to include running 7 | # the tests. 8 | JAVA_PROJECT_PATH=../../java 9 | 10 | # Directory where build package should be placed. 11 | BUILD_TARGET=target 12 | # Place where the produced Jar files should go. 13 | JAVA_BUILD_TARGET=inst/java 14 | 15 | all: build 16 | 17 | build: $(BUILD_TARGET) tests docs 18 | 19 | # Create portable bundle package ready to be installed. 20 | $(BUILD_TARGET): build-subprojects 21 | rm -rf $(JAVA_BUILD_TARGET) 22 | mkdir -p $(JAVA_BUILD_TARGET) 23 | cp $(JAVA_PROJECT_PATH)/target/*jar-with-dependencies.jar $(JAVA_BUILD_TARGET) 24 | mkdir -p $(BUILD_TARGET) 25 | Rscript -e "devtools::build(path = \"$(BUILD_TARGET)\")" 26 | 27 | # Build subprojects. We assume that testing is a part of their building process. 28 | build-subprojects: 29 | $(MAKE) -C $(JAVA_PROJECT_PATH) build 30 | 31 | # Run all tests. Tests in subprojects are not run explicitly because we assume 32 | # that building them requires them to pass tests anyway. 33 | test: $(BUILD_TARGET) 34 | # Rscript run_all_tests.R 35 | 36 | # Generate documentation. 37 | docs: 38 | Rscript -e "devtools::document()" 39 | 40 | # Check the code and package structure for common problems; run tests. 41 | # The number of ERRORs and WARNINGs should be zero. Ideally, the number of 42 | # NOTE's also should be zero. Currently there's one NOTE that says that the 43 | # paths to some of the files are too long (see README). 44 | check: build 45 | Rscript -e "devtools::check()" 46 | 47 | # Install the package in the system. 48 | install: test docs 49 | R CMD INSTALL . 50 | 51 | # Uninstall the package. 52 | uninstall: 53 | R CMD REMOVE $(PACKAGE_NAME) 54 | 55 | clean: clean-subprojects 56 | rm -rf man NAMESPACE *.tar.gz 57 | rm -rf $(JAVA_BUILD_TARGET) 58 | rm -rf $(BUILD_TARGET) 59 | 60 | clean-subprojects: 61 | $(MAKE) -C $(JAVA_PROJECT_PATH) clean 62 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(generateBpmnLayout) 4 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/R/bpmnLayoutGeneratoR.R: -------------------------------------------------------------------------------- 1 | #' @export 2 | generateBpmnLayout <- function(flow_node, sequence_flow, outputType = "BPMN") { 3 | bpmnLayoutJava <- rJava::.jnew("io/process/analytics/tools/bpmn/generator/BPMNLayoutGenerator") 4 | type <- rJava::J(class = "io/process/analytics.tools/bpmn/generator/BPMNLayoutGenerator$ExportType", "valueOf", outputType) 5 | 6 | flow_node_as_csv <- paste(to_csv(flow_node), collapse = "\n") 7 | sequence_flow_as_csv <- paste(to_csv(sequence_flow), collapse = "\n") 8 | 9 | tryCatch( diagramAsString <- bpmnLayoutJava$generateLayoutFromCSV(flow_node_as_csv, sequence_flow_as_csv, type), Exception = function(e){ 10 | e$printStackTrace() 11 | # raise the error 12 | stop("BPMN Layout generation fails. See Java stack trace.") 13 | } ) 14 | return(diagramAsString) 15 | } 16 | 17 | to_csv <- function(x) { 18 | connection <- textConnection("fn_as_csv", "w", local = TRUE) 19 | utils::write.csv(x, connection) 20 | return(fn_as_csv) 21 | } 22 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/R/init.R: -------------------------------------------------------------------------------- 1 | .onLoad <- function(libname, pkgname) { 2 | rJava::.jinit() 3 | rJava::.jpackage(pkgname, lib.loc = libname) 4 | rJava::.jaddClassPath(paste(libname, "/", pkgname, "/", "java")) 5 | writeLines("Using classpath") 6 | writeLines(rJava::.jclassPath()) 7 | } -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/README.adoc: -------------------------------------------------------------------------------- 1 | = bpmnLayoutGeneratoR 2 | 3 | This library wraps the bpmn-layout-generator java library 4 | 5 | 6 | == Prerequisites 7 | 8 | `bpmnLayoutGeneratoR` depends on http://rforge.net/rJava/:[rJava]. 9 | 10 | You must install a JDK to make `rJava` work. For the JDK installation, you can use for instance 11 | 12 | * your OS package installer 13 | * Adoptium: https://adoptium.net/installation/ 14 | 15 | Once the JDK is installed, don't forget to run `R CMD javareconf`. See https://cran.r-project.org/doc/manuals/R-admin.html#Java-support 16 | 17 | For more details, please refer to the http://rforge.net/rJava/:[rJava documentation] and the https://github.com/s-u/rJava/blob/master/README.md[rJava GitHub README]. 18 | 19 | == Usage 20 | 21 | Install the package. `bpmnLayoutGeneratoR-x.y.z` is the version you want to install, see the available branches on GitHub 22 | 23 | [source,R] 24 | ---- 25 | # install the package 26 | devtools::install_github("process-analytics/bpmn-layout-generators", ref="bpmnLayoutGeneratoR-x.y.z", subdir="R/bpmnLayoutGeneratoR") 27 | ---- 28 | 29 | Then load data and generate the layout 30 | 31 | [source,R] 32 | ---- 33 | # example source sequence_flow and flow_node 34 | flow_node <- data.frame(id=c(1,2),node=c("task1","task2"),type=c("task","task")) 35 | sequence_flow <- data.frame(id=1,from_id=1,to_id=2) 36 | 37 | # Call the library and generate the diagram using "ASCII", "BPMN" or "SVG". Defaults to "BPMN". 38 | diagram <- bpmnLayoutGeneratoR::generateBpmnLayout(flow_node, sequence_flow, "BPMN") 39 | ---- 40 | 41 | If you want to display the BPMN diagram, you can install the https://github.com/process-analytics/bpmn-visualization-R[bpmnVisualization] package and then run 42 | [source,R] 43 | ---- 44 | bpmnVisualization::display(diagram) 45 | ---- 46 | 47 | The preview displays something like 48 | 49 | image::img/readme_bpmn_display.png[BPMN display in R preview] 50 | 51 | [#release] 52 | == Build and release new version 53 | 54 | [NOTE] 55 | ==== 56 | Generally, the R release follow the Java release and is fully automated, so there is nothing to do. 57 | For more details, see the xref:../../README.md[overall release process]. 58 | 59 | In this case, the branch mentioned here must be created from the related tag. 60 | 61 | Otherwise, it has to be created from the `master` branch. 62 | ==== 63 | 64 | The following is for a manual release: 65 | 66 | * create a branch named `bpmnLayoutGeneratoR-x.y.z` (replace with version) 67 | * execute `make install` in the directory `R/bpmnLayoutGeneratoR` 68 | * commit and push everything with title `release: bpmnLayoutGeneratoR-x.y.z` 69 | 70 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/bpmnLayoutGeneratoR.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /R/bpmnLayoutGeneratoR/img/readme_bpmn_display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/R/bpmnLayoutGeneratoR/img/readme_bpmn_display.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

BPMN Layout Generators


2 |

3 |

4 | 5 | GitHub release (latest by date including pre-releases 6 | 7 | 8 | Build 9 | 10 |
11 | 16 | 17 | Contributor Covenant 18 | 19 | 20 | License 21 | 22 |

23 |

24 | 25 | **:warning: THIS IS AN EXPERIMENTAL PROJECT :warning:** 26 | 27 | Tools for generating the graphical layout of the BPMN process (BPMNDI Graphical Model) in BPMN files. 28 | They can be used with diagrams containing only the BPMN Semantic part or to replace the existing BPMNDI part. 29 | 30 | 31 | 32 | ## Implementations 33 | 34 | > [!NOTE] 35 | > The implementations are based or partially based on https://www.researchgate.net/publication/221542866_A_Simple_Algorithm_for_Automatic_Layout_of_BPMN_Processes 36 | 37 | Available implementations: 38 | - [java](java/README.md) 39 | - [R](R/bpmnLayoutGeneratoR/README.adoc) This is only a wrapper of the java library 40 | 41 | 42 | ## Existing alternatives 43 | 44 | Java 45 | - https://github.com/camunda-consulting/code/tree/382f1521a4e9cd6bb92c2f9eacbe64a0e3835242/snippets/bpmndi-generator (latest available commit on 2020-04-08) 46 | - https://github.com/camunda-consulting/migrate-to-camunda-tools/: tools to migrate from several vendors to Camunda, adaptation of the `bpmndi-generator` 47 | - Camunda `fluent builder API`: https://docs.camunda.org/manual/7.9/user-guide/model-api/bpmn-model-api/fluent-builder-api/#generation-of-diagram-interchange 48 | 49 | Javascript 50 | - https://github.com/bpmn-io/bpmn-auto-layout/ 51 | 52 | 53 | ## 🚀 Release how-to 54 | 55 | > [!IMPORTANT] 56 | > Follow the paragraphs in the order in which they are documented. 57 | 58 | ### Preparation 59 | 60 | #### Decide on the value of the new version to be released 61 | 62 | > [!NOTE] 63 | > The versioning follows **semver** (be aware of the rules for 0.x.y versions). 64 | 65 | Go to the page of the last [release](https://github.com/process-analytics/bpmn-layout-generators/releases/latest) to know what the latest version was. 66 | 67 | Check for changes since the release of the last version: 68 | 69 | ![](docs/images/release_01_find_commits_since_previous_release.png) 70 | 71 | Check the commit list to determine the type of version to release (major, minor or patch) based on changes and the value of the previous version (follow semver rules): 72 | 73 | ![](docs/images/release_02_list_commits_since_previous_release.png) 74 | 75 | The version you've just determined should follow the form `x.y.z`, so keep this in mind for future tasks. 76 | 77 | #### Prepare the release notes 78 | 79 | The GitHub release will include an auto-generated release notes which is based on the labels of the merged Pull Requests. 80 | 81 | Ensure that all merged PR included in the release are labelled. You can find the [unlabeled PR](https://github.com/process-analytics/bpmn-layout-generators/pulls?q=is%3Apr+sort%3Aupdated-desc+no%3Alabel+is%3Amerged) to easily labeled them. 82 | 83 | 84 | ### Perform the release 85 | 86 | When all updates have been completed, you are ready to publish a new release. 87 | 88 | Go to the [release workflow](https://github.com/process-analytics/bpmn-layout-generators/actions/workflows/release.yml) in GitHub Actions and run it by choosing the version to release. 89 | 90 | This workflow: 91 | - Creates a Git tag 92 | - Triggers the creation of the release branch of the R package 93 | - Triggers the creation of a draft GitHub release 94 | 95 | ### Publish the release notes 96 | 97 | The release workflow has initiated a new draft GitHub release, which needs to be updated and published : 98 | - For more details about GitHub release, follow the [GitHub help](https://help.github.com/en/github/administering-a-repository/managing-releases-in-a-repository#creating-a-release): 99 | - The release notes has been [automatically generated](https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes). Review and adjust it if necessary. 100 | - Publish the GitHub release 101 | 102 | 103 | ## License 104 | 105 | `bpmn-layout-generators` is released under the [Apache 2.0](LICENSE) license. \ 106 | Copyright © 2020-present, Bonitasoft S.A. 107 | -------------------------------------------------------------------------------- /docs/images/release_01_find_commits_since_previous_release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/docs/images/release_01_find_commits_since_previous_release.png -------------------------------------------------------------------------------- /docs/images/release_02_list_commits_since_previous_release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/docs/images/release_02_list_commits_since_previous_release.png -------------------------------------------------------------------------------- /java/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /java/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: target/*.jar 4 | 5 | target/*.jar: pom.xml $(shell find src -name '*') 6 | ./mvnw package 7 | 8 | test: 9 | ./mvnw test 10 | 11 | clean: 12 | rm -rf target 13 | -------------------------------------------------------------------------------- /java/README.md: -------------------------------------------------------------------------------- 1 | # BPMN Layout Generator Java Implementation 2 | 3 | ## Requirements 4 | 5 | To build and run, use: 6 | > JDK 17 or JDK 21 7 | 8 | > [!WARNING] 9 | > The build produces bytecode for JDK 17 or newer, so you cannot run the provided jar with older JDK versions. 10 | 11 | 12 | ## Get the bpmn-layout-generator jar 13 | 14 | Choose one of the following options: 15 | 16 | ### Download 17 | 18 | The jar is available in the release branches of the [R package](../R/bpmnLayoutGeneratoR/README.adoc). 19 | 20 | For example, for version `0.1.4`, you can download the jar from https://github.com/process-analytics/bpmn-layout-generators/tree/bpmnLayoutGeneratoR-0.1.4/R/bpmnLayoutGeneratoR/inst/java. 21 | 22 | 23 | ### Build 24 | 25 | > [!NOTE] 26 | > Building the jar let you use the latest version of the code. 27 | 28 | The project bundles a Maven Wrapper, so just run 29 | ``` bash 30 | ./mvnw package 31 | ``` 32 | 33 | ## Usage 34 | 35 | **Note**: for more options, run with the `--help` option 36 | 37 | To generate the layout of an existing BPMN file and save the result as a BPMN file, run 38 | ``` 39 | java -jar target/bpmn-layout-generator-*-jar-with-dependencies.jar --output= 40 | ``` 41 | If you want to have the resulting layout in an SVG file, pass `--output-type=SVG` 42 | Notice that `ASCII` and `SVG` output types have been developed to get feedback when running tests i.e. to get a quick preview of the 43 | algorithm result. They are not fully implemented and won't probably never be (if you have some interest on that 44 | topic, feel free to provide a Pull Request) 45 | 46 | 47 | To generate BPMN semantic and diagram layout from discovery CSV files, run 48 | ``` 49 | java -jar target/bpmn-layout-generator-*-jar-with-dependencies.jar \ 50 | --input-type=CSV \ 51 | --output= \ 52 | csv/PatientsProcess/nodeSimple.csv csv/PatientsProcess/edgeSimple.cs 53 | ``` 54 | 55 | 56 | ## Release 57 | 58 | The release process is fully automated. See the [release process](../README.md) for more details. 59 | -------------------------------------------------------------------------------- /java/mvnw.cmd: -------------------------------------------------------------------------------- 1 | <# : batch portion 2 | @REM ---------------------------------------------------------------------------- 3 | @REM Licensed to the Apache Software Foundation (ASF) under one 4 | @REM or more contributor license agreements. See the NOTICE file 5 | @REM distributed with this work for additional information 6 | @REM regarding copyright ownership. The ASF licenses this file 7 | @REM to you under the Apache License, Version 2.0 (the 8 | @REM "License"); you may not use this file except in compliance 9 | @REM with the License. You may obtain a copy of the License at 10 | @REM 11 | @REM http://www.apache.org/licenses/LICENSE-2.0 12 | @REM 13 | @REM Unless required by applicable law or agreed to in writing, 14 | @REM software distributed under the License is distributed on an 15 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | @REM KIND, either express or implied. See the License for the 17 | @REM specific language governing permissions and limitations 18 | @REM under the License. 19 | @REM ---------------------------------------------------------------------------- 20 | 21 | @REM ---------------------------------------------------------------------------- 22 | @REM Apache Maven Wrapper startup batch script, version 3.3.2 23 | @REM 24 | @REM Optional ENV vars 25 | @REM MVNW_REPOURL - repo url base for downloading maven distribution 26 | @REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven 27 | @REM MVNW_VERBOSE - true: enable verbose log; others: silence the output 28 | @REM ---------------------------------------------------------------------------- 29 | 30 | @IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) 31 | @SET __MVNW_CMD__= 32 | @SET __MVNW_ERROR__= 33 | @SET __MVNW_PSMODULEP_SAVE=%PSModulePath% 34 | @SET PSModulePath= 35 | @FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( 36 | IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) 37 | ) 38 | @SET PSModulePath=%__MVNW_PSMODULEP_SAVE% 39 | @SET __MVNW_PSMODULEP_SAVE= 40 | @SET __MVNW_ARG0_NAME__= 41 | @SET MVNW_USERNAME= 42 | @SET MVNW_PASSWORD= 43 | @IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) 44 | @echo Cannot start maven from wrapper >&2 && exit /b 1 45 | @GOTO :EOF 46 | : end batch / begin powershell #> 47 | 48 | $ErrorActionPreference = "Stop" 49 | if ($env:MVNW_VERBOSE -eq "true") { 50 | $VerbosePreference = "Continue" 51 | } 52 | 53 | # calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties 54 | $distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl 55 | if (!$distributionUrl) { 56 | Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" 57 | } 58 | 59 | switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { 60 | "maven-mvnd-*" { 61 | $USE_MVND = $true 62 | $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" 63 | $MVN_CMD = "mvnd.cmd" 64 | break 65 | } 66 | default { 67 | $USE_MVND = $false 68 | $MVN_CMD = $script -replace '^mvnw','mvn' 69 | break 70 | } 71 | } 72 | 73 | # apply MVNW_REPOURL and calculate MAVEN_HOME 74 | # maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ 75 | if ($env:MVNW_REPOURL) { 76 | $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } 77 | $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" 78 | } 79 | $distributionUrlName = $distributionUrl -replace '^.*/','' 80 | $distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' 81 | $MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" 82 | if ($env:MAVEN_USER_HOME) { 83 | $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" 84 | } 85 | $MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' 86 | $MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" 87 | 88 | if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { 89 | Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" 90 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 91 | exit $? 92 | } 93 | 94 | if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { 95 | Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" 96 | } 97 | 98 | # prepare tmp dir 99 | $TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile 100 | $TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" 101 | $TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null 102 | trap { 103 | if ($TMP_DOWNLOAD_DIR.Exists) { 104 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 105 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 106 | } 107 | } 108 | 109 | New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null 110 | 111 | # Download and Install Apache Maven 112 | Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." 113 | Write-Verbose "Downloading from: $distributionUrl" 114 | Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" 115 | 116 | $webclient = New-Object System.Net.WebClient 117 | if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { 118 | $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) 119 | } 120 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 121 | $webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null 122 | 123 | # If specified, validate the SHA-256 sum of the Maven distribution zip file 124 | $distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum 125 | if ($distributionSha256Sum) { 126 | if ($USE_MVND) { 127 | Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." 128 | } 129 | Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash 130 | if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { 131 | Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." 132 | } 133 | } 134 | 135 | # unzip and move 136 | Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null 137 | Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null 138 | try { 139 | Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null 140 | } catch { 141 | if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { 142 | Write-Error "fail to move MAVEN_HOME" 143 | } 144 | } finally { 145 | try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } 146 | catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } 147 | } 148 | 149 | Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" 150 | -------------------------------------------------------------------------------- /java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | io.process.analytics.tools.bpmn 7 | bpmn-layout-generator 8 | 0.2.1-SNAPSHOT 9 | 10 | 11 | UTF-8 12 | 17 13 | 17 14 | 15 | 5.10.2 16 | 17 | 18 | 19 | 20 | org.projectlombok 21 | lombok 22 | 1.18.34 23 | provided 24 | 25 | 26 | jakarta.xml.bind 27 | jakarta.xml.bind-api 28 | 2.3.3 29 | 30 | 31 | com.sun.xml.bind 32 | jaxb-impl 33 | 2.3.3 34 | 35 | 36 | 37 | 38 | org.apache.logging.log4j 39 | log4j-core 40 | 2.23.1 41 | 42 | 43 | info.picocli 44 | picocli 45 | 4.7.5 46 | 47 | 48 | 49 | org.junit.jupiter 50 | junit-jupiter-api 51 | ${junit.version} 52 | test 53 | 54 | 55 | org.junit.jupiter 56 | junit-jupiter-engine 57 | ${junit.version} 58 | test 59 | 60 | 61 | org.junit.jupiter 62 | junit-jupiter-params 63 | ${junit.version} 64 | test 65 | 66 | 67 | org.assertj 68 | assertj-core 69 | 3.25.3 70 | test 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | maven-assembly-plugin 80 | 3.7.1 81 | 82 | 83 | maven-clean-plugin 84 | 3.3.2 85 | 86 | 87 | maven-resources-plugin 88 | 3.3.1 89 | 90 | 91 | maven-compiler-plugin 92 | 3.13.0 93 | 94 | 95 | maven-surefire-plugin 96 | 3.3.0 97 | 98 | 99 | maven-jar-plugin 100 | 3.4.2 101 | 102 | 103 | maven-install-plugin 104 | 3.1.2 105 | 106 | 107 | maven-deploy-plugin 108 | 3.1.2 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-assembly-plugin 116 | 117 | 118 | package 119 | 120 | single 121 | 122 | 123 | 124 | 125 | io.process.analytics.tools.bpmn.generator.App 126 | 127 | 128 | 129 | jar-with-dependencies 130 | 131 | 132 | 133 | 134 | 135 | 136 | org.codehaus.mojo 137 | jaxb2-maven-plugin 138 | 2.5.0 139 | 140 | 141 | xjc 142 | 143 | xjc 144 | 145 | 146 | 147 | 148 | io.process.analytics.tools.bpmn.generator.internal.generated.model 149 | en 150 | 151 | 152 | 153 | 154 | org.codehaus.mojo 155 | build-helper-maven-plugin 156 | 3.6.0 157 | 158 | 159 | add-source 160 | generate-sources 161 | 162 | add-source 163 | 164 | 165 | 166 | ${project.build.directory}/generate-sources/jaxb 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/App.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package io.process.analytics.tools.bpmn.generator; 16 | 17 | import static java.lang.String.format; 18 | import static java.util.Arrays.stream; 19 | import static java.util.stream.Collectors.joining; 20 | 21 | import java.io.File; 22 | import java.nio.file.Files; 23 | import java.nio.file.NoSuchFileException; 24 | import java.util.concurrent.Callable; 25 | 26 | import io.process.analytics.tools.bpmn.generator.BPMNLayoutGenerator.ExportType; 27 | import io.process.analytics.tools.bpmn.generator.internal.FileUtils; 28 | import lombok.extern.log4j.Log4j2; 29 | import picocli.CommandLine; 30 | import picocli.CommandLine.Command; 31 | import picocli.CommandLine.Option; 32 | import picocli.CommandLine.Parameters; 33 | 34 | @Log4j2 35 | @Command(name = "App", mixinStandardHelpOptions = true) 36 | public class App implements Callable { 37 | 38 | 39 | @Option(names = {"-t", "--input-type"}, 40 | description = "BPMN or CSV.", 41 | paramLabel = "TYPE") 42 | String inputType = "BPMN"; 43 | @Option(names = {"-u", "--output-type"}, 44 | description = "BPMN, SVG or ASCII.", 45 | paramLabel = "TYPE") 46 | String outputType = "BPMN"; 47 | 48 | @Option(names = {"-o", "--output"}, 49 | description = "Output file.", 50 | paramLabel = "OUTPUT") 51 | private File outputFile; 52 | @Parameters(arity = "1..2", paramLabel = "INPUT", description = "Input file(s).") 53 | private File[] inputFiles; 54 | 55 | public static void main(String[] args) throws Exception { 56 | int exitCode = runApp(args); 57 | System.exit(exitCode); 58 | } 59 | 60 | static int runApp(String... args) { 61 | return new CommandLine(new App()).execute(args); 62 | } 63 | 64 | 65 | private App() { 66 | } 67 | 68 | @Override 69 | public Integer call() { 70 | try { 71 | BPMNLayoutGenerator bpmnLayoutGenerator = new BPMNLayoutGenerator(); 72 | String output; 73 | switch (inputType) { 74 | case "BPMN": 75 | if (inputFiles.length != 1) { 76 | System.err.println("Expected only one input file to import from BPMN format, got: " + inputType.length()); 77 | } 78 | output = bpmnLayoutGenerator.generateLayoutFromBPMNSemantic(FileUtils.fileContent(inputFiles[0]), exportType(outputType)); 79 | break; 80 | case "CSV": 81 | if (inputFiles.length != 2) { 82 | System.err.println("Expected 2 input files to import from CSV format, got: " + inputType.length()); 83 | } 84 | output = bpmnLayoutGenerator.generateLayoutFromCSV(FileUtils.fileContent(inputFiles[0]), FileUtils.fileContent(inputFiles[1]), exportType(outputType)); 85 | break; 86 | default: 87 | System.err.println("Unexpected input type: " + inputType); 88 | return 2; 89 | } 90 | if (outputFile != null) { 91 | FileUtils.touch(outputFile); 92 | Files.write(outputFile.toPath(), output.getBytes()); 93 | } else { 94 | System.out.println(output); 95 | } 96 | } catch (NoSuchFileException e) { 97 | System.err.println("File not found: " + e.getMessage()); 98 | return 2; 99 | } catch (Exception e) { 100 | System.err.println("An error occurred: " + e.getMessage()); 101 | return 1; 102 | } 103 | return 0; 104 | } 105 | 106 | 107 | private ExportType exportType(String arg) { 108 | try { 109 | return ExportType.valueOf(arg.toUpperCase()); 110 | } catch (IllegalArgumentException e) { 111 | throw new IllegalArgumentException( 112 | format("Invalid export type: %s. Must be one of [%s]", arg, 113 | stream(ExportType.values()) 114 | .map(Enum::toString) 115 | .map(String::toLowerCase) 116 | .collect(joining(", ")))); 117 | } 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/BPMNLayoutGenerator.java: -------------------------------------------------------------------------------- 1 | package io.process.analytics.tools.bpmn.generator; 2 | 3 | import static io.process.analytics.tools.bpmn.generator.export.BPMNExporter.defaultBpmnExporter; 4 | import static io.process.analytics.tools.bpmn.generator.internal.BpmnInOut.defaultBpmnInOut; 5 | 6 | import io.process.analytics.tools.bpmn.generator.algo.ShapeLayouter; 7 | import io.process.analytics.tools.bpmn.generator.algo.ShapeSorter; 8 | import io.process.analytics.tools.bpmn.generator.converter.BpmnToAlgoModelConverter; 9 | import io.process.analytics.tools.bpmn.generator.export.ASCIIExporter; 10 | import io.process.analytics.tools.bpmn.generator.export.SVGExporter; 11 | import io.process.analytics.tools.bpmn.generator.input.CSVtoBPMN; 12 | import io.process.analytics.tools.bpmn.generator.internal.BpmnInOut; 13 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.TDefinitions; 14 | import io.process.analytics.tools.bpmn.generator.model.Diagram; 15 | import io.process.analytics.tools.bpmn.generator.model.Grid; 16 | import lombok.Getter; 17 | import lombok.RequiredArgsConstructor; 18 | import lombok.extern.log4j.Log4j2; 19 | 20 | @Log4j2 21 | public class BPMNLayoutGenerator { 22 | 23 | 24 | public enum ExportType { 25 | ASCII, 26 | BPMN, 27 | SVG 28 | } 29 | 30 | protected final BpmnInOut bpmnInOut = defaultBpmnInOut(); 31 | 32 | 33 | /* 34 | Public methods 35 | */ 36 | 37 | public String generateLayoutFromBPMNSemantic(String bpmn, ExportType exportType) { 38 | TDefinitions tDefinitions = bpmnInOut.readFromBpmn(bpmn); 39 | LayoutSortedDiagram layout = layout(tDefinitions); 40 | return export(layout, exportType); 41 | } 42 | 43 | public String generateLayoutFromCSV(String nodes, String edges, ExportType exportType) { 44 | TDefinitions tDefinitions = new CSVtoBPMN().readFromCSV(nodes, edges); 45 | LayoutSortedDiagram layout = layout(tDefinitions); 46 | return export(layout, exportType); 47 | } 48 | 49 | 50 | /* 51 | BPMN --> Diagram 52 | */ 53 | 54 | private LayoutSortedDiagram layout(TDefinitions definitions) { 55 | log.info("Converting BPMN into internal model"); 56 | Diagram diagram = new BpmnToAlgoModelConverter().toAlgoModel(definitions); 57 | log.info("Conversion done"); 58 | 59 | log.info("Sorting and generating Layout"); 60 | Diagram sortedDiagram = new ShapeSorter().sort(diagram); 61 | Grid grid = new ShapeLayouter().layout(sortedDiagram); 62 | log.info("Sort and Layout done"); 63 | 64 | return new LayoutSortedDiagram(definitions, grid, sortedDiagram); 65 | } 66 | 67 | 68 | 69 | 70 | /* 71 | Diagram --> Exported format 72 | */ 73 | 74 | private String exportToAscii(LayoutSortedDiagram diagram) { 75 | log.info("Exporting to ASCII file"); 76 | return new ASCIIExporter().export(diagram.getGrid()); 77 | } 78 | 79 | protected String exportToBpmn(LayoutSortedDiagram diagram) { 80 | log.info("Exporting to BPMN"); 81 | TDefinitions newDefinitions = defaultBpmnExporter().export(diagram.originalDefinitions, diagram.grid, diagram.diagram); 82 | return bpmnInOut.writeToBpmn(newDefinitions); 83 | } 84 | 85 | private String exportToSvg(LayoutSortedDiagram diagram) { 86 | log.info("Exporting to SVG"); 87 | return new SVGExporter().export(diagram.getGrid(), diagram.getDiagram()); 88 | } 89 | 90 | 91 | private String export(LayoutSortedDiagram layout, ExportType exportType) { 92 | return switch (exportType) { 93 | case ASCII -> exportToAscii(layout); 94 | case BPMN -> exportToBpmn(layout); 95 | case SVG -> exportToSvg(layout); 96 | default -> throw new IllegalStateException("Unexpected Export Type: " + exportType); 97 | }; 98 | } 99 | 100 | @RequiredArgsConstructor 101 | @Getter 102 | // TODO switch to record 103 | public static class LayoutSortedDiagram { 104 | 105 | private final TDefinitions originalDefinitions; 106 | private final Grid grid; 107 | private final Diagram diagram; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/algo/ShapeLayouter.java: -------------------------------------------------------------------------------- 1 | package io.process.analytics.tools.bpmn.generator.algo; 2 | 3 | import static io.process.analytics.tools.bpmn.generator.export.ASCIIExporter.toAscii; 4 | import static io.process.analytics.tools.bpmn.generator.model.Position.position; 5 | 6 | import java.util.List; 7 | 8 | import io.process.analytics.tools.bpmn.generator.model.Edge; 9 | import io.process.analytics.tools.bpmn.generator.model.Grid; 10 | import io.process.analytics.tools.bpmn.generator.model.Shape; 11 | import io.process.analytics.tools.bpmn.generator.model.Position; 12 | import io.process.analytics.tools.bpmn.generator.model.Diagram; 13 | import lombok.extern.log4j.Log4j2; 14 | 15 | @Log4j2 16 | public class ShapeLayouter { 17 | 18 | 19 | public Grid layout(Diagram diagram) { 20 | Grid grid = new Grid(); 21 | for (Shape shape : diagram.getShapes()) { 22 | Position positionOfCurrentShape = positionShape(diagram, grid, shape); 23 | putOnGrid(grid, positionOfCurrentShape); 24 | // TODO check usage of supplier, intellij says it is deprecated 25 | log.debug("Adding {}:\n{}", shape::getName, () -> toAscii(grid)); 26 | addRowsWhenShapeIsASplit(diagram, grid, shape, positionOfCurrentShape); 27 | } 28 | compactGrid(grid); 29 | log.debug("After compact grid \n{}", () -> toAscii(grid)); 30 | return grid; 31 | } 32 | 33 | private void putOnGrid(Grid grid, Position positionOfCurrentShape) { 34 | if (grid.isFilled(positionOfCurrentShape)) { 35 | //never overlap an element 36 | grid.addRowAfter(positionOfCurrentShape.getY()); 37 | grid.add(positionOfCurrentShape.toBuilder().y(positionOfCurrentShape.getY() + 1).build()); 38 | } else { 39 | grid.add(positionOfCurrentShape); 40 | } 41 | } 42 | 43 | private void addRowsWhenShapeIsASplit(Diagram diagram, Grid grid, Shape shape, Position positionOfCurrentShape) { 44 | List outgoingEdges = diagram.getOutgoingEdges(shape.getId()); 45 | if (outgoingEdges.size() > 1) { 46 | //add rows to place elements of this split 47 | int rowsToAddBeforeAndAfter = outgoingEdges.size() / 2; 48 | for (int i = 0; i < rowsToAddBeforeAndAfter; i++) { 49 | grid.addRowAfter(positionOfCurrentShape.getY()); 50 | grid.addRowBefore(positionOfCurrentShape.getY()); 51 | } 52 | } 53 | } 54 | 55 | private Position positionShape(Diagram diagram, Grid grid, Shape shape) { 56 | Position positionOfCurrentShape; 57 | List incomingEdges = diagram.getIncomingEdges(shape.getId()); 58 | if (incomingEdges.isEmpty()) { 59 | //This is a start node, insert it in a new column 60 | positionOfCurrentShape = addStartShape(grid, shape); 61 | } else if (incomingEdges.size() == 1) { 62 | //find the previous node position 63 | String previousShapeID = incomingEdges.get(0).getFrom(); 64 | List outgoingEdgesOfPreviousShape = diagram.getOutgoingEdges(previousShapeID); 65 | if (outgoingEdgesOfPreviousShape.size() == 1) { 66 | positionOfCurrentShape = addDirectlyNextTo(grid, shape, previousShapeID); 67 | } else { 68 | positionOfCurrentShape = addSplit(grid, shape, previousShapeID, outgoingEdgesOfPreviousShape); 69 | } 70 | } else { 71 | positionOfCurrentShape = addJoin(grid, shape, incomingEdges); 72 | } 73 | return positionOfCurrentShape; 74 | } 75 | 76 | private void compactGrid(Grid grid) { 77 | int i = 0; 78 | while (i < grid.getLastRowIndex()) { 79 | List currentRow = grid.getRow(i).stream().map(Position::getX).toList(); 80 | List nextRow = grid.getRow(i + 1).stream().map(Position::getX).toList(); 81 | 82 | boolean currentRowCanBeMovedBelow = true; 83 | for (Integer shapeIndexInCurrentRow : currentRow) { 84 | int index = shapeIndexInCurrentRow; 85 | //we can move the current row below if each element have all adjacent cells free in the row below 86 | if (nextRow.stream().anyMatch(s -> s == index || s == index + 1 || s == index - 1)) { 87 | currentRowCanBeMovedBelow = false; 88 | break; 89 | } 90 | } 91 | 92 | if (currentRowCanBeMovedBelow) { 93 | final int finalI = i + 1; 94 | for (Position position : grid.getRow(i)) { 95 | grid.remove(position); 96 | grid.add(position.toBuilder().y(finalI).build()); 97 | } 98 | grid.removeEmptyRow(i); 99 | } else { 100 | i++; 101 | } 102 | } 103 | } 104 | 105 | private Position addStartShape(Grid grid, Shape shape) { 106 | return position(shape, 0, grid.getLastRowIndex() + 1); 107 | } 108 | 109 | private Position addSplit(Grid grid, Shape shape, String previousShapeID, List outgoingEdgesOfPreviousShape) { 110 | Position previousShapePosition = grid.getPosition(previousShapeID); 111 | int numberOfShapesInTheSplit = outgoingEdgesOfPreviousShape.size(); 112 | int indexOfCurrentShape = outgoingEdgesOfPreviousShape.stream().map(Edge::getTo).toList().indexOf(shape.getId()); 113 | //put element right to the split vertically distributed according to the index 114 | int relativeYPosition; 115 | if (numberOfShapesInTheSplit % 2 == 0 && indexOfCurrentShape >= numberOfShapesInTheSplit / 2) { 116 | //if there is an even number of element, there is no "middle" element so we must add 1 to the index of the elements after the "middle" 117 | relativeYPosition = indexOfCurrentShape + 1 - numberOfShapesInTheSplit / 2; 118 | } else { 119 | relativeYPosition = indexOfCurrentShape - numberOfShapesInTheSplit / 2; 120 | } 121 | return position(shape, previousShapePosition.getX() + 1, previousShapePosition.getY() + relativeYPosition); 122 | } 123 | 124 | private Position addJoin(Grid grid, Shape shape, List incomingEdges) { 125 | //first implementation: middle of elements it joins 126 | // later we should also try yo find the split to align it to that if possible 127 | List positions = incomingEdges.stream().map(Edge::getFrom).map(grid::getPosition).toList(); 128 | int xMax = positions.stream().map(Position::getX).reduce(0, Math::max); 129 | int yMax = positions.stream().map(Position::getY).reduce(0, Math::max); 130 | int yMin = positions.stream().map(Position::getY).reduce(Integer.MAX_VALUE, Math::min); 131 | 132 | int xElement = xMax + 1; 133 | int yElement = (yMin + yMax) / 2; 134 | if ((yMin + yMax) % 2 != 0) { 135 | grid.addRowAfter(yElement); 136 | yElement++; 137 | } 138 | return position(shape, xElement, yElement); 139 | } 140 | 141 | private Position addDirectlyNextTo(Grid grid, Shape shapeToAdd, String rightTo) { 142 | Position previous = grid.getPosition(rightTo); 143 | return position(shapeToAdd, previous.getX() + 1, previous.getY()); 144 | } 145 | 146 | 147 | } 148 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/AlgoToDisplayModelConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.converter; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.converter.Configuration.CELL_HEIGHT; 19 | import static io.process.analytics.tools.bpmn.generator.converter.Configuration.CELL_WIDTH; 20 | 21 | import java.util.Collection; 22 | import java.util.stream.Collectors; 23 | 24 | import io.process.analytics.tools.bpmn.generator.converter.waypoint.WayPointsComputer; 25 | import io.process.analytics.tools.bpmn.generator.model.*; 26 | import io.process.analytics.tools.bpmn.generator.model.display.*; 27 | 28 | public class AlgoToDisplayModelConverter { 29 | 30 | public DisplayModel convert(Grid grid, Diagram diagram) { 31 | DisplayModel.DisplayModelBuilder model = DisplayModel.builder(); 32 | // dimensions must be increased when generating alternate path to avoid edge overlapping on shapes 33 | // mainly impact on svg exporter. If the dimensions are not updated, the alternate paths are not fully displayed 34 | // as they are out of the viewport of the svg 35 | // increase to display edges with extra paths to avoid shape overlapping 36 | model.width(grid.width() * CELL_WIDTH).height((grid.height() + 1) * CELL_HEIGHT); 37 | 38 | Collection flowNodes = grid.getPositions().stream() 39 | .map(position -> toDisplayFlowNode(position, diagram)) 40 | .collect(Collectors.toList()); 41 | model.flowNodes(flowNodes); 42 | 43 | WayPointsComputer wayPointsComputer = new WayPointsComputer(grid, flowNodes); 44 | diagram.getEdges() 45 | .stream() 46 | .map(edge -> new DisplayEdge(edge.getId(), wayPointsComputer.compute(edge))) 47 | .forEach(model::edge); 48 | 49 | return model.build(); 50 | } 51 | 52 | private DisplayFlowNode toDisplayFlowNode(Position position, Diagram diagram) { 53 | int xOffset = position.getX() * CELL_WIDTH; 54 | int yOffset = position.getY() * CELL_HEIGHT; 55 | int nodeWidth = x(60); 56 | int nodeHeight = y(60); 57 | 58 | // TODO manage when not found (should not occur) 59 | Shape shape = diagram.getShapes().stream() 60 | .filter(s -> s.getId().equals(position.getShape())) 61 | .findFirst().get(); 62 | String name = shape.getName(); 63 | 64 | // ensure to have a square shape (i.e. same width and height) for non activity elements 65 | ShapeType shapeType = shape.getType(); 66 | if (shapeType == ShapeType.EVENT || shapeType == ShapeType.GATEWAY) { 67 | int nodeDimension = Math.min(nodeWidth, nodeHeight); 68 | if (shapeType == ShapeType.EVENT) { 69 | nodeDimension /= 2; 70 | } 71 | nodeWidth = nodeDimension; 72 | nodeHeight = nodeDimension; 73 | } 74 | 75 | int x = xOffset + (CELL_WIDTH - nodeWidth) / 2; 76 | int y = yOffset + (CELL_HEIGHT - nodeHeight) / 2; 77 | DisplayDimension flowNodeDimension = new DisplayDimension(x, y, nodeWidth, nodeHeight); 78 | 79 | // Labels positions better work with the SVG export 80 | // BPMN label positions are adjusted in BPMNDiagramRichBuilder 81 | int labelX = xOffset + x(50); 82 | int labelY = yOffset + y(50); 83 | if (shapeType == ShapeType.EVENT) { // put the label under the shape 84 | labelY = (int) (y + nodeHeight * 1.5); 85 | } else if (shapeType == ShapeType.GATEWAY) { // put the label on the top left of the shape 86 | labelX = (int) (x - nodeWidth * 0.5); 87 | labelY = (int) (y - nodeHeight * 0.5); 88 | } 89 | 90 | DisplayDimension labelDimension = new DisplayDimension(labelX, labelY, nodeWidth, nodeHeight); 91 | DisplayLabel label = new DisplayLabel(name, y(16), labelDimension); 92 | 93 | return DisplayFlowNode.builder().bpmnElementId(shape.getId()) 94 | .dimension(flowNodeDimension) 95 | .label(label) 96 | .type(shapeType) 97 | .rx(y(10)) 98 | .strokeWidth(y(5)) 99 | .build(); 100 | } 101 | 102 | private static int x(int percentage) { 103 | return CELL_WIDTH * percentage / 100; 104 | } 105 | 106 | private static int y(int percentage) { 107 | return CELL_HEIGHT * percentage / 100; 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/BpmnToAlgoModelConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.converter; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.internal.Semantic.getId; 19 | import static io.process.analytics.tools.bpmn.generator.model.ShapeType.*; 20 | 21 | import java.util.List; 22 | 23 | import io.process.analytics.tools.bpmn.generator.internal.Semantic; 24 | import io.process.analytics.tools.bpmn.generator.internal.Semantic.BpmnElements; 25 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.*; 26 | import io.process.analytics.tools.bpmn.generator.model.Edge; 27 | import io.process.analytics.tools.bpmn.generator.model.Diagram; 28 | import io.process.analytics.tools.bpmn.generator.model.Shape; 29 | import io.process.analytics.tools.bpmn.generator.model.ShapeType; 30 | 31 | public class BpmnToAlgoModelConverter { 32 | 33 | public Diagram toAlgoModel(TDefinitions definitions) { 34 | Semantic semantic = new Semantic(definitions); 35 | Diagram.DiagramBuilder diagram = Diagram.builder(); 36 | 37 | List processes = semantic.getProcesses(); 38 | for (TProcess process : processes) { 39 | BpmnElements bpmnElements = semantic.getBpmnElements(process); 40 | 41 | bpmnElements.getFlowNodes() 42 | .stream() 43 | .map(BpmnToAlgoModelConverter::toShape) 44 | .forEach(diagram::shape); 45 | 46 | bpmnElements.getSequenceFlows() 47 | .stream() 48 | .map(seqFlow -> Edge.edge(seqFlow.getId(), getId(seqFlow.getSourceRef()), getId(seqFlow.getTargetRef()))) 49 | .forEach(diagram::edge); 50 | } 51 | 52 | return diagram.build(); 53 | } 54 | 55 | // visible for testing 56 | static Shape toShape(TFlowElement flowNode) { 57 | ShapeType shapeType = ACTIVITY; 58 | boolean isSplitGateway = false; 59 | if (flowNode instanceof TGateway) { 60 | shapeType = GATEWAY; 61 | isSplitGateway = ((TGateway) flowNode).getOutgoing().size() > 1; 62 | } else if (flowNode instanceof TEvent) { 63 | shapeType = EVENT; 64 | } 65 | return new Shape(flowNode.getId(), flowNode.getName(), shapeType, isSplitGateway); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/Configuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.converter; 14 | 15 | public class Configuration { 16 | // TODO this should be configurable for by the client code 17 | public static final int CELL_WIDTH = 200; 18 | public static final int CELL_HEIGHT = 100; 19 | 20 | public static final int EDGE_OUTGOING_FIRST_HORIZONTAL_SEGMENT_LENGTH = 20; 21 | } 22 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/BendConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 17 | 18 | import lombok.Builder; 19 | import lombok.ToString; 20 | 21 | @Builder 22 | @ToString 23 | public class BendConfiguration { 24 | public final BendDirection direction; 25 | public final int offset; 26 | } 27 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/BendDirection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 17 | 18 | public enum BendDirection { 19 | 20 | BOTTOM, TOP; 21 | 22 | public int numericFactor() { 23 | return this == BOTTOM ? 1 : -1; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/Direction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 17 | 18 | public enum Direction { 19 | LeftToRight, RightToLeft, // generally for horizontal orientation 20 | BottomToTop, TopToBottom, // generally for vertical orientation 21 | BottomLeftToTopRight, TopRightToBottomLeft, BottomRightToTopLeft, TopLeftToBottomRight 22 | } 23 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/EdgeTerminalPoints.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 14 | 15 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayDimension; 16 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayPoint; 17 | 18 | /** 19 | * Compute coordinates of the terminal points (source or target) of an Edge. 20 | */ 21 | public class EdgeTerminalPoints { 22 | 23 | public DisplayPoint rightMiddle(DisplayDimension dimension) { 24 | return new DisplayPoint(dimension.x + dimension.width, dimension.y + dimension.height / 2); 25 | } 26 | 27 | public DisplayPoint leftMiddle(DisplayDimension dimension) { 28 | return new DisplayPoint(dimension.x, dimension.y + dimension.height / 2); 29 | } 30 | 31 | public DisplayPoint centerBottom(DisplayDimension dimension) { 32 | return new DisplayPoint(dimension.x + dimension.width / 2, dimension.y + dimension.height); 33 | } 34 | 35 | public DisplayPoint centerTop(DisplayDimension dimension) { 36 | return new DisplayPoint(dimension.x + dimension.width / 2, dimension.y); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/GridSearcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 14 | 15 | import io.process.analytics.tools.bpmn.generator.model.Edge; 16 | import io.process.analytics.tools.bpmn.generator.model.Grid; 17 | import io.process.analytics.tools.bpmn.generator.model.Position; 18 | import lombok.RequiredArgsConstructor; 19 | import lombok.extern.log4j.Log4j2; 20 | 21 | @RequiredArgsConstructor 22 | @Log4j2 23 | public class GridSearcher { 24 | 25 | private final Grid grid; 26 | 27 | public Position getPositionFrom(Edge edge) { 28 | return getPosition(edge, true); 29 | } 30 | 31 | public Position getPositionTo(Edge edge) { 32 | return getPosition(edge, false); 33 | } 34 | 35 | private Position getPosition(Edge edge, boolean isFrom) { 36 | String shapeId = isFrom && !edge.isReverted() || (!isFrom && edge.isReverted()) ? edge.getFrom() : edge.getTo(); 37 | return getPositionOfShape(shapeId); 38 | } 39 | 40 | private Position getPositionOfShape(String shapeId) { 41 | return grid.getPositions() 42 | .stream() 43 | .filter(p -> shapeId.equals(p.getShape())) 44 | .findFirst() 45 | .get(); // always exist, otherwise error occur on flow node generation 46 | } 47 | 48 | public boolean isShapeExistAtLeft(Position position) { 49 | return grid.getPositions() 50 | .stream() 51 | .filter(p -> p.getY() == position.getY()) 52 | .anyMatch(p -> p.getX() == position.getX() - 1); 53 | } 54 | 55 | public boolean isShapeExistAtRight(final Position position) { 56 | return grid.getPositions() 57 | .stream() 58 | .filter(p -> p.getY() == position.getY()) 59 | .anyMatch(p -> p.getX() == position.getX() + 1); 60 | } 61 | 62 | public boolean isShapeExistAbove(final Position positionFrom) { 63 | return grid.getPositions() 64 | .stream() 65 | .filter(p -> p.getX() == positionFrom.getX()) 66 | .anyMatch(p -> p.getY() == positionFrom.getY() - 1); 67 | } 68 | 69 | public BendConfiguration computeConfigurationToPassByEmptyRow(Position positionFrom, Position positionTo, 70 | BendDirection searchDirection) { 71 | int positionFromY = positionFrom.getY(); 72 | int offset = -1; 73 | boolean isElementBetween = true; 74 | 75 | log.debug("Search empty rows in direction {}", searchDirection); 76 | while (isElementBetween) { 77 | offset++; 78 | isElementBetween = this.hasElementsBetweenPositionsHorizontally( 79 | positionFromY + searchDirection.numericFactor() * offset, 80 | positionFrom.getX(), positionTo.getX()); 81 | } 82 | log.debug("Found empty row at offset {}", offset); 83 | return BendConfiguration.builder().direction(searchDirection).offset(offset).build(); 84 | } 85 | 86 | private boolean hasElementsBetweenPositionsHorizontally(final int y, final int x1, final int x2) { 87 | log.debug("Searching for elements horizontally between positions. y={} x1={} x2={}", y, x1, x2); 88 | return grid.getPositions() 89 | .stream() 90 | .filter(p -> p.getY() == y) 91 | .filter(p -> p.getX() > Math.min(x1, x2)) 92 | .anyMatch(p -> p.getX() < Math.max(x1, x2)); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/Orientation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 17 | 18 | public enum Orientation { 19 | Horizontal, Vertical, HorizontalVertical, VerticalHorizontal, // standard orientations 20 | VerticalHorizontalVertical // extra orientations to avoid edges overlapping shapes 21 | } 22 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/WayPointDescriptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 14 | 15 | import lombok.Builder; 16 | import lombok.ToString; 17 | 18 | @Builder 19 | @ToString 20 | public class WayPointDescriptor { 21 | 22 | public final Direction direction; 23 | public final Orientation orientation; 24 | public final BendConfiguration bendConfiguration; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/WayPointsComputer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 14 | 15 | import java.util.Collection; 16 | import java.util.List; 17 | 18 | import io.process.analytics.tools.bpmn.generator.model.Edge; 19 | import io.process.analytics.tools.bpmn.generator.model.Grid; 20 | import io.process.analytics.tools.bpmn.generator.model.Position; 21 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayFlowNode; 22 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayPoint; 23 | import lombok.extern.log4j.Log4j2; 24 | 25 | @Log4j2 26 | public class WayPointsComputer { 27 | 28 | private final Collection flowNodes; 29 | private final GridSearcher gridSearcher; 30 | private final WayPointsConverter wayPointsConverter = new WayPointsConverter(); 31 | private final WayPointsPositioner wayPointsPositioner; 32 | 33 | public WayPointsComputer(final Grid grid, final Collection flowNodes) { 34 | this.flowNodes = flowNodes; 35 | gridSearcher = new GridSearcher(grid); 36 | wayPointsPositioner = new WayPointsPositioner(gridSearcher); 37 | } 38 | 39 | public List compute(Edge edge) { 40 | log.debug("Inferring waypoints of edge {}", edge); 41 | Position positionFrom = gridSearcher.getPositionFrom(edge); 42 | Position positionTo = gridSearcher.getPositionTo(edge); 43 | 44 | WayPointDescriptor wayPointDescriptor = wayPointsPositioner.computeWaypointDescriptor(positionFrom, positionTo); 45 | DisplayFlowNode flowNodeFrom = getFlowNode(positionFrom.getShape()); 46 | DisplayFlowNode flowNodeTo = getFlowNode(positionTo.getShape()); 47 | 48 | return wayPointsConverter.toDisplayPoints(wayPointDescriptor, flowNodeFrom.dimension, flowNodeTo.dimension); 49 | } 50 | 51 | private DisplayFlowNode getFlowNode(String flowNodeId) { 52 | return flowNodes.stream() 53 | .filter(f -> flowNodeId.equals(f.bpmnElementId)) 54 | .findFirst() 55 | .get(); // always exist, otherwise error occur on flow node generation 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/converter/waypoint/WayPointsPositioner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.converter.waypoint; 14 | 15 | import static io.process.analytics.tools.bpmn.generator.converter.waypoint.Direction.*; 16 | import static io.process.analytics.tools.bpmn.generator.converter.waypoint.Orientation.*; 17 | 18 | import io.process.analytics.tools.bpmn.generator.converter.waypoint.WayPointDescriptor.WayPointDescriptorBuilder; 19 | import io.process.analytics.tools.bpmn.generator.model.Position; 20 | import io.process.analytics.tools.bpmn.generator.model.ShapeType; 21 | import lombok.RequiredArgsConstructor; 22 | import lombok.extern.log4j.Log4j2; 23 | 24 | @RequiredArgsConstructor 25 | @Log4j2 26 | public class WayPointsPositioner { 27 | 28 | private final GridSearcher gridSearcher; 29 | 30 | public WayPointDescriptor computeWaypointDescriptor(Position positionFrom, Position positionTo) { 31 | WayPointDescriptorBuilder builder = WayPointDescriptor.builder(); 32 | 33 | if (positionFrom.getX() == positionTo.getX()) { 34 | builder.orientation(Vertical); 35 | if (positionFrom.getY() < positionTo.getY()) { 36 | builder.direction(TopToBottom); 37 | } else { 38 | builder.direction(BottomToTop); 39 | } 40 | } else if (positionFrom.getY() == positionTo.getY()) { 41 | builder.orientation(Horizontal); 42 | int positionFromX = positionFrom.getX(); 43 | int positionToX = positionTo.getX(); 44 | 45 | if (positionFromX < positionToX) { 46 | builder.direction(LeftToRight); 47 | if (positionFromX + 1 != positionToX) { 48 | updateWhenExtraBendPointsRequired(builder, positionFrom, positionTo); 49 | } 50 | } else { 51 | builder.direction(RightToLeft); 52 | if (positionFromX - 1 != positionToX) { 53 | updateWhenExtraBendPointsRequired(builder, positionFrom, positionTo); 54 | } 55 | } 56 | } else if (positionFrom.getX() < positionTo.getX()) { 57 | if (positionFrom.getY() < positionTo.getY()) { 58 | builder.direction(TopLeftToBottomRight); 59 | boolean shapeExistAtRightPositionFrom = gridSearcher.isShapeExistAtRight(positionFrom); 60 | Orientation orientation = shapeExistAtRightPositionFrom 61 | || (isGatewayAt(positionFrom) && isGatewaySplitAt(positionFrom) && (!isGatewayAt(positionTo) || isGatewaySplitAt(positionTo))) 62 | ? VerticalHorizontal 63 | : HorizontalVertical; 64 | builder.orientation(orientation); 65 | } else { 66 | builder.direction(BottomLeftToTopRight); 67 | if (isGatewayAt(positionFrom)) { 68 | Orientation orientation = !isGatewaySplitAt(positionFrom) 69 | || isGatewayAt(positionTo) && !isGatewaySplitAt(positionTo) 70 | ? HorizontalVertical 71 | : VerticalHorizontal; 72 | builder.orientation(orientation); 73 | } else { 74 | boolean shapeExistAbovePositionFrom = gridSearcher.isShapeExistAbove(positionFrom); 75 | Orientation orientation = shapeExistAbovePositionFrom || isGatewayAt(positionTo) 76 | ? HorizontalVertical 77 | : VerticalHorizontal; 78 | builder.orientation(orientation); 79 | } 80 | } 81 | } else { 82 | if (positionFrom.getY() < positionTo.getY()) { 83 | builder.direction(TopRightToBottomLeft); 84 | boolean shapeExistAtLeftPositionFrom = gridSearcher.isShapeExistAtLeft(positionFrom); 85 | Orientation orientation = shapeExistAtLeftPositionFrom || isGatewaySplitAt(positionFrom) 86 | ? VerticalHorizontal 87 | : HorizontalVertical; 88 | builder.orientation(orientation); 89 | } else { 90 | builder.direction(BottomRightToTopLeft); 91 | boolean shapeExistAbovePositionFrom = gridSearcher.isShapeExistAbove(positionFrom); 92 | Orientation orientation = shapeExistAbovePositionFrom || isGatewayAt(positionTo) 93 | ? HorizontalVertical 94 | : VerticalHorizontal; 95 | builder.orientation(orientation); 96 | } 97 | } 98 | 99 | WayPointDescriptor wayPointDescriptor = builder.build(); 100 | log.debug("Computed {}", wayPointDescriptor); 101 | return wayPointDescriptor; 102 | } 103 | 104 | private void updateWhenExtraBendPointsRequired(WayPointDescriptorBuilder builder, Position positionFrom, 105 | Position positionTo) { 106 | log.debug("Detected potential overlapping of horizontal edge on shapes. Shape: {}", positionFrom.getShape()); 107 | 108 | BendConfiguration bendConfigurationBottom = gridSearcher.computeConfigurationToPassByEmptyRow(positionFrom, positionTo, 109 | BendDirection.BOTTOM); 110 | if (bendConfigurationBottom.offset == 0) { 111 | log.debug("Keep the horizontal orientation"); 112 | return; 113 | } 114 | log.debug("Not possible to keep horizontal orientation"); 115 | builder.orientation(VerticalHorizontalVertical); 116 | builder.bendConfiguration(bendConfigurationBottom); 117 | 118 | if (bendConfigurationBottom.offset == 1) { 119 | return; 120 | } 121 | 122 | BendConfiguration bendConfigurationTop = gridSearcher.computeConfigurationToPassByEmptyRow(positionFrom, positionTo, 123 | BendDirection.TOP); 124 | if (bendConfigurationTop.offset < bendConfigurationBottom.offset) { 125 | builder.bendConfiguration(bendConfigurationTop); 126 | } 127 | } 128 | 129 | private static boolean isGatewayAt(Position position) { 130 | return ShapeType.GATEWAY.equals(position.getShapeType()); 131 | } 132 | 133 | private static boolean isGatewaySplitAt(Position position) { 134 | return isGatewayAt(position) && position.isSplitGateway(); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/export/ASCIIExporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.export; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.internal.StringUtils.defaultIfNull; 19 | 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | import io.process.analytics.tools.bpmn.generator.model.Grid; 24 | import io.process.analytics.tools.bpmn.generator.model.Position; 25 | 26 | public class ASCIIExporter { 27 | 28 | private static final int CELL_WIDTH = 7; 29 | 30 | public String export(Grid grid) { 31 | int width = grid.width(); 32 | int height = grid.height(); 33 | List positions = grid.getPositions(); 34 | return export(width, height, positions); 35 | } 36 | 37 | public String export(int width, int height, List positions) { 38 | char[][] charGrid = new char[height][width * CELL_WIDTH]; 39 | for (char[] chars : charGrid) { 40 | Arrays.fill(chars, ' '); 41 | } 42 | for (Position position : positions) { 43 | char[] charRow = charGrid[position.getY()]; 44 | char[] name = defaultIfNull(position.getShapeName()).toCharArray(); 45 | System.arraycopy(name, 0, charRow, 7 * position.getX(), Math.min(name.length, CELL_WIDTH)); 46 | } 47 | StringBuilder content = new StringBuilder("Diagram:\n"); 48 | content.append('+').append(horizontalBar(CELL_WIDTH * width)).append('+').append('\n'); 49 | for (char[] chars : charGrid) { 50 | content.append('|').append(chars).append('|').append('\n'); 51 | } 52 | content.append('+').append(horizontalBar(CELL_WIDTH * width)).append('+').append('\n'); 53 | return content.toString(); 54 | } 55 | 56 | private char[] horizontalBar(int size) { 57 | char[] chars = new char[size]; 58 | Arrays.fill(chars, '-'); 59 | return chars; 60 | } 61 | 62 | public static String toAscii(Grid grid) { 63 | return new ASCIIExporter().export(grid); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/export/BPMNExporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.export; 17 | 18 | import io.process.analytics.tools.bpmn.generator.converter.AlgoToDisplayModelConverter; 19 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayModel; 20 | import io.process.analytics.tools.bpmn.generator.internal.BPMNDiagramRichBuilder; 21 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.TDefinitions; 22 | import io.process.analytics.tools.bpmn.generator.model.Diagram; 23 | import io.process.analytics.tools.bpmn.generator.model.Grid; 24 | import lombok.RequiredArgsConstructor; 25 | 26 | @RequiredArgsConstructor 27 | public class BPMNExporter { 28 | 29 | private final AlgoToDisplayModelConverter converter; 30 | 31 | public static BPMNExporter defaultBpmnExporter() { 32 | return new BPMNExporter(new AlgoToDisplayModelConverter()); 33 | } 34 | 35 | public TDefinitions export(TDefinitions originalBpmnDefinitions, Grid grid, Diagram diagram) { 36 | BPMNDiagramRichBuilder builder = new BPMNDiagramRichBuilder(originalBpmnDefinitions); 37 | DisplayModel displayModel = converter.convert(grid, diagram); 38 | displayModel.flowNodes.forEach(builder::addFlowNode); 39 | displayModel.edges.forEach(builder::addEdge); 40 | return builder.build(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/input/CSVtoBPMN.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | package io.process.analytics.tools.bpmn.generator.input; 16 | 17 | import io.process.analytics.tools.bpmn.generator.internal.Semantic; 18 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.*; 19 | 20 | import javax.xml.XMLConstants; 21 | import javax.xml.namespace.QName; 22 | import java.util.ArrayList; 23 | import java.util.HashMap; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | import static io.process.analytics.tools.bpmn.generator.internal.Semantic.addFlowNodes; 28 | import static io.process.analytics.tools.bpmn.generator.internal.Semantic.addSequenceFlows; 29 | 30 | public class CSVtoBPMN { 31 | 32 | // map original flow element id with the values we are using 33 | // id cannot be numeric, in that case we map the id with a generated one, letting edge reference element ids with the values we are using for BPMN 34 | private final Map mappingShapeId = new HashMap<>(); 35 | 36 | // map flow element id with its incoming and outgoing edges 37 | private final Map shapeRelations = new HashMap<>(); 38 | 39 | public TDefinitions readFromCSV(String nodes, String edges) { 40 | TProcess process = new TProcess(); 41 | process.setId("process_1"); 42 | TDefinitions definitions = new TDefinitions(); 43 | definitions.setId("definitions_1"); 44 | definitions.setTargetNamespace(XMLConstants.NULL_NS_URI); 45 | Semantic semantic = new Semantic(definitions); 46 | semantic.add(process); 47 | 48 | List flowNodeElements = getFlowNodeElements(nodes); 49 | addFlowNodes(process, flowNodeElements); 50 | addSequenceFlows(process, getEdgeElements(edges)); 51 | assignIncomingAndOutgoingReferences(flowNodeElements); 52 | 53 | return definitions; 54 | } 55 | 56 | private List getFlowNodeElements(String nodes) { 57 | String[] lines = toLinesWithoutHeader(nodes); 58 | List flowElements = new ArrayList<>(); 59 | for (String line : lines) { 60 | if (line == null) { 61 | continue; 62 | } 63 | String[] node = line.split(","); 64 | TFlowNode flowNode = gettFlowNode(node); 65 | flowNode.setName(removeEnclosingDoubleQuote(node[2])); 66 | 67 | String originalId = node[1]; 68 | String bpmnId = originalId; 69 | if (isNumeric(originalId)) { 70 | bpmnId = "bpmnElement_" + originalId; 71 | } 72 | this.mappingShapeId.put(originalId, bpmnId); 73 | 74 | flowNode.setId(bpmnId); 75 | flowElements.add(flowNode); 76 | } 77 | return flowElements; 78 | } 79 | 80 | private static TFlowNode gettFlowNode(String[] node) { 81 | String type = removeEnclosingDoubleQuote(node[3]); 82 | TFlowNode flowNode = switch (type) { 83 | case "start_event" -> new TStartEvent(); 84 | case "end_event" -> new TEndEvent(); 85 | case "gateway", "parallel_gateway" -> new TParallelGateway(); 86 | case "exclusive_gateway" -> new TExclusiveGateway(); 87 | case "inclusive_gateway" -> new TInclusiveGateway(); 88 | case "user_task" -> new TUserTask(); 89 | case "service_task" -> new TServiceTask(); 90 | default -> new TTask(); 91 | }; 92 | return flowNode; 93 | } 94 | 95 | private void assignIncomingAndOutgoingReferences(List flowNodeElements) { 96 | for (TFlowNode flowNode : flowNodeElements) { 97 | EdgeRelation edgeRelation = this.shapeRelations.get(flowNode.getId()); 98 | edgeRelation.incoming.stream().map(CSVtoBPMN::bpmnElementQName).forEach(flowNode.getIncoming()::add); 99 | edgeRelation.outgoing.stream().map(CSVtoBPMN::bpmnElementQName).forEach(flowNode.getOutgoing()::add); 100 | } 101 | } 102 | 103 | // TODO rework to find the right value 104 | private static QName bpmnElementQName(String bpmnElement) { 105 | return new QName(XMLConstants.NULL_NS_URI, bpmnElement, XMLConstants.DEFAULT_NS_PREFIX); // sequenceFlow_1 106 | // return new QName("http://www.omg.org/spec/BPMN/20100524/MODEL", bpmnElement, XMLConstants.DEFAULT_NS_PREFIX); // semantic:sequenceFlow_1 107 | } 108 | 109 | private static String removeEnclosingDoubleQuote(String s) { 110 | return s.replaceAll("^\"|\"$", ""); 111 | } 112 | 113 | private String[] toLinesWithoutHeader(String fileContent) { 114 | String[] lines = fileContent.split("\n"); 115 | lines[0] = null; 116 | return lines; 117 | } 118 | 119 | private List getEdgeElements(String edges) { 120 | String[] lines = toLinesWithoutHeader(edges); 121 | List flowElements = new ArrayList<>(); 122 | for (String line : lines) { 123 | if(line == null){ 124 | continue; 125 | } 126 | String[] edge = line.split(","); 127 | TSequenceFlow tSequenceFlow = new TSequenceFlow(); 128 | 129 | TUserTask sourceRef = new TUserTask(); 130 | tSequenceFlow.setSourceRef(sourceRef); 131 | String sourceRefId = this.mappingShapeId.getOrDefault(edge[2], edge[2]); 132 | sourceRef.setId(sourceRefId); 133 | 134 | TUserTask targetRef = new TUserTask(); 135 | tSequenceFlow.setTargetRef(targetRef); 136 | String targetRefId = this.mappingShapeId.getOrDefault(edge[3], edge[3]); 137 | targetRef.setId(targetRefId); 138 | 139 | String sequenceFlowId = edge[1]; 140 | if (isNumeric(sequenceFlowId)) { 141 | sequenceFlowId = "sequenceFlow_" + sequenceFlowId; 142 | } 143 | tSequenceFlow.setId(sequenceFlowId); 144 | flowElements.add(tSequenceFlow); 145 | 146 | // prepare incoming/outgoing management 147 | getEdgeRelation(sourceRefId).outgoing.add(sequenceFlowId); 148 | getEdgeRelation(targetRefId).incoming.add(sequenceFlowId); 149 | } 150 | return flowElements; 151 | } 152 | 153 | private EdgeRelation getEdgeRelation(String shapeId) { 154 | EdgeRelation edgeRelation = this.shapeRelations.get(shapeId); 155 | if (edgeRelation == null) { 156 | edgeRelation= new EdgeRelation(); 157 | this.shapeRelations.put(shapeId, edgeRelation); 158 | } 159 | return edgeRelation; 160 | } 161 | 162 | // TODO not optimal for performance, see https://www.baeldung.com/java-check-string-number 163 | // bpmn element id cannot be numeric 164 | private static boolean isNumeric(String s) { 165 | if (s == null) { 166 | return false; 167 | } 168 | try { 169 | Double.parseDouble(s); 170 | } catch (NumberFormatException nfe) { 171 | return false; 172 | } 173 | return true; 174 | } 175 | 176 | private static class EdgeRelation { 177 | 178 | public final List incoming = new ArrayList<>(); 179 | public final List outgoing = new ArrayList<>(); 180 | 181 | } 182 | 183 | } 184 | 185 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/BPMNDiagramRichBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.Optional; 21 | 22 | import javax.xml.XMLConstants; 23 | import javax.xml.bind.JAXBElement; 24 | import javax.xml.namespace.QName; 25 | 26 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayDimension; 27 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayEdge; 28 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayFlowNode; 29 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayPoint; 30 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.*; 31 | import io.process.analytics.tools.bpmn.generator.model.ShapeType; 32 | import lombok.NonNull; 33 | import lombok.RequiredArgsConstructor; 34 | 35 | /** 36 | * Helper to build a BPMNDiagram based on existing TDefinitions semantic part 37 | */ 38 | @RequiredArgsConstructor 39 | public class BPMNDiagramRichBuilder { 40 | 41 | @NonNull 42 | private final TDefinitions definitions; 43 | 44 | private final List bpmnShapes = new ArrayList<>(); 45 | private final List bpmnEdges = new ArrayList<>(); 46 | 47 | // visible for testing 48 | BPMNDiagram initializeBPMNDiagram() { 49 | BPMNDiagram bpmnDiagram = new BPMNDiagram(); 50 | bpmnDiagram.setId("BPMNDiagram_1"); 51 | 52 | BPMNPlane bpmnPlane = new BPMNPlane(); 53 | bpmnDiagram.setBPMNPlane(bpmnPlane); 54 | bpmnPlane.setId("BPMNPlane_1"); 55 | putBpmnElement(bpmnPlane, computeBPMNPlaneBpmnElementId()); 56 | 57 | return bpmnDiagram; 58 | } 59 | 60 | public void addFlowNode(DisplayFlowNode flowNode) { 61 | BPMNShape bpmnShape = new BPMNShape(); 62 | String bpmnElementId = flowNode.bpmnElementId; 63 | bpmnShape.setId("BPMNShape_" + bpmnElementId); 64 | putBpmnElement(bpmnShape, bpmnElementId); 65 | 66 | bpmnShape.setBounds(bounds(flowNode.dimension)); 67 | 68 | // For activity, don't pass label position, BPMN vendor generally manage default positionning very well (centered on activity inside) 69 | ShapeType shapeType = flowNode.type; 70 | if (!ShapeType.ACTIVITY.equals(shapeType)) { 71 | BPMNLabel label = new BPMNLabel(); 72 | DisplayDimension labelDimension = flowNode.label.dimension; 73 | 74 | // For event adjust positions 75 | if(ShapeType.EVENT == shapeType) { 76 | labelDimension = new DisplayDimension( flowNode.dimension.x, labelDimension.y, labelDimension.width, labelDimension.height); 77 | } 78 | 79 | label.setBounds(bounds(labelDimension)); 80 | // TODO add label style? 81 | bpmnShape.setBPMNLabel(label); 82 | } 83 | 84 | bpmnShapes.add(bpmnShape); 85 | } 86 | 87 | public void addEdge(DisplayEdge edge) { 88 | BPMNEdge bpmnEdge = new BPMNEdge(); 89 | String bpmnElementId = edge.bpmnElementId; 90 | bpmnEdge.setId("BPMNEdge_" + bpmnElementId); 91 | putBpmnElement(bpmnEdge, bpmnElementId); 92 | 93 | List bpmnEdgeWaypoint = bpmnEdge.getWaypoint(); 94 | edge.wayPoints.stream() 95 | .map(BPMNDiagramRichBuilder::toPoint) 96 | .forEach(bpmnEdgeWaypoint::add); 97 | 98 | bpmnEdges.add(bpmnEdge); 99 | } 100 | 101 | private static Point toPoint(DisplayPoint displayPoint) { 102 | Point point = new Point(); 103 | point.setX(displayPoint.x); 104 | point.setY(displayPoint.y); 105 | return point; 106 | } 107 | 108 | private static Bounds bounds(DisplayDimension dimension) { 109 | Bounds bounds = new Bounds(); 110 | bounds.setX(dimension.x); 111 | bounds.setY(dimension.y); 112 | bounds.setWidth(dimension.width); 113 | bounds.setHeight(dimension.height); 114 | return bounds; 115 | } 116 | 117 | private static void putBpmnElement(BPMNShape bpmnShape, String bpmnElementRef) { 118 | bpmnShape.setBpmnElement(noNamespaceQName(bpmnElementRef)); 119 | } 120 | 121 | private static QName noNamespaceQName(String bpmnElementRef) { 122 | return new QName(XMLConstants.NULL_NS_URI, bpmnElementRef); 123 | } 124 | 125 | private static void putBpmnElement(BPMNEdge bpmnEdge, String bpmnElementRef) { 126 | bpmnEdge.setBpmnElement(noNamespaceQName(bpmnElementRef)); 127 | } 128 | 129 | public TDefinitions build() { 130 | // TODO it should be better to clone the initial definitions 131 | List diagrams = definitions.getBPMNDiagram(); 132 | diagrams.clear(); 133 | BPMNDiagram bpmnDiagram = initializeBPMNDiagram(); 134 | diagrams.add(bpmnDiagram); 135 | 136 | BPMNPlane bpmnPlane = bpmnDiagram.getBPMNPlane(); 137 | List> diagramElements = bpmnPlane.getDiagramElement(); 138 | 139 | bpmnShapes.stream() 140 | .map(s -> new JAXBElement<>(bpmnElementQName("BPMNShape"), BPMNShape.class, null, s)) 141 | .forEach(diagramElements::add); 142 | 143 | bpmnEdges.stream() 144 | .map(s -> new JAXBElement<>(bpmnElementQName("BPMNEdge"), BPMNEdge.class, null, s)) 145 | .forEach(diagramElements::add); 146 | 147 | return definitions; 148 | } 149 | 150 | private static QName bpmnElementQName(String bpmnElement) { 151 | return new QName("http://www.omg.org/spec/BPMN/20100524/DI", bpmnElement, XMLConstants.DEFAULT_NS_PREFIX); 152 | } 153 | 154 | // TODO we need to know if this is a process or a collaboration to set the actual QName 155 | private static void putBpmnElement(BPMNPlane bpmnPlane, String bpmnElementRef) { 156 | bpmnPlane.setBpmnElement(noNamespaceQName(bpmnElementRef)); 157 | } 158 | 159 | // TODO move to the Semantic class? 160 | // bpmn element value depends on semantic collaboration existence 161 | // if no collaboration, there is a single process, so use its id 162 | private String computeBPMNPlaneBpmnElementId() { 163 | final Semantic semantic = new Semantic(definitions); 164 | Optional collaboration = semantic.getCollaboration(); 165 | // TODO empty processes is supposed to be managed by Semantic 166 | return collaboration.map(TBaseElement::getId) 167 | .orElseGet(() -> semantic.getProcesses().get(0).getId()); 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/BpmnInOut.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.internal.FileUtils.fileContent; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | 23 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.TDefinitions; 24 | 25 | public class BpmnInOut { 26 | 27 | private final XmlParser xmlParser; 28 | 29 | public static BpmnInOut defaultBpmnInOut() { 30 | return new BpmnInOut(new XmlParser()); 31 | } 32 | 33 | public BpmnInOut(XmlParser xmlParser) { 34 | this.xmlParser = xmlParser; 35 | } 36 | 37 | public TDefinitions readFromBpmn(String xml) { 38 | return xmlParser.unmarshall(xml); 39 | } 40 | 41 | public TDefinitions readFromBpmn(File bpmn) { 42 | try { 43 | String xml = fileContent(bpmn); 44 | return readFromBpmn(xml); 45 | } catch (IOException e) { 46 | throw new RuntimeException("Error while reading file " + bpmn.getName(), e); 47 | } 48 | } 49 | 50 | public String writeToBpmn(TDefinitions definitions) { 51 | return xmlParser.marshal(definitions); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/BpmnNamespacePrefixMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import com.sun.xml.bind.marshaller.NamespacePrefixMapper; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.Set; 23 | 24 | public class BpmnNamespacePrefixMapper extends NamespacePrefixMapper { 25 | 26 | private static final Map namespaces = new HashMap<>(); 27 | static { 28 | namespaces.put("http://www.omg.org/spec/BPMN/20100524/DI", "bpmndi"); 29 | namespaces.put("http://www.omg.org/spec/BPMN/20100524/MODEL", "semantic"); 30 | namespaces.put("http://www.omg.org/spec/DD/20100524/DC", "dc"); 31 | namespaces.put("http://www.omg.org/spec/DD/20100524/DI", "di"); 32 | } 33 | 34 | @Override 35 | public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) { 36 | return namespaces.getOrDefault(namespaceUri, suggestion); 37 | } 38 | 39 | @Override 40 | public String[] getPreDeclaredNamespaceUris() { 41 | Set uris = namespaces.keySet(); 42 | return uris.toArray(new String[0]); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/FileUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.nio.file.Files; 21 | import java.util.List; 22 | 23 | public class FileUtils { 24 | 25 | // when switching to JDK11+, use Files#readString instead 26 | public static String fileContent(File file) throws IOException { 27 | List strings = Files.readAllLines(file.toPath()); 28 | // we do not care of having a OS related eol as the xml reader can handle whatever eol, so use an hardcoded eol 29 | return String.join("\n", strings); 30 | } 31 | 32 | public static void createParents(File file) { 33 | // TODO throw IOException if failure like commons-io 34 | file.getParentFile().mkdirs(); 35 | } 36 | 37 | public static void touch(File file) throws IOException { 38 | createParents(file); 39 | file.createNewFile(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/IdUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.internal; 14 | 15 | import java.util.UUID; 16 | 17 | public class IdUtils { 18 | 19 | public static String generateRandomId() { 20 | return UUID.randomUUID().toString(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/Semantic.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import java.util.*; 19 | import java.util.stream.Collectors; 20 | 21 | import javax.xml.XMLConstants; 22 | import javax.xml.bind.JAXBElement; 23 | import javax.xml.namespace.QName; 24 | 25 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.*; 26 | import lombok.Getter; 27 | import lombok.NonNull; 28 | import lombok.RequiredArgsConstructor; 29 | 30 | /** 31 | * Helper to access to the BPMN semantic part 32 | */ 33 | @RequiredArgsConstructor 34 | // TODO switch to record 35 | // TODO lombok getter on class 36 | public class Semantic { 37 | 38 | @NonNull 39 | @Getter 40 | private final TDefinitions definitions; 41 | 42 | // assuming this is a TBaseElement 43 | public static String getId(Object object) { 44 | return ((TBaseElement) object).getId(); 45 | } 46 | 47 | public Optional getCollaboration() { 48 | List collaborations = definitions.getRootElement().stream() 49 | .map(JAXBElement::getValue) 50 | .filter(TCollaboration.class::isInstance) 51 | .map(TCollaboration.class::cast) 52 | .toList(); 53 | 54 | // TODO check at most 1 otherwise error 55 | // TODO refactor into a more functional way 56 | if (collaborations.isEmpty()) { 57 | return Optional.empty(); 58 | } 59 | return Optional.of(collaborations.get(0)); 60 | } 61 | 62 | public List getProcesses() { 63 | // TODO manage no process (should not occurred) 64 | return definitions.getRootElement().stream() 65 | .map(JAXBElement::getValue) 66 | .filter(TProcess.class::isInstance) 67 | .map(TProcess.class::cast) 68 | .collect(Collectors.toList()); 69 | } 70 | 71 | // TODO manage lanes 72 | public BpmnElements getBpmnElements(TProcess process) { 73 | // TODO manage TLaneSet 74 | // List laneSet = process.getLaneSet(); 75 | // // there is always a TLaneSet (see BPMN spec) 76 | // List lanes = laneSet.get(0).getLane(); 77 | // // TODO do this for all lanes 78 | // // TODO have a look at childLaneSet 79 | // List> flowNodeRef = lanes.get(0).getFlowNodeRef(); 80 | 81 | 82 | List> flowElements = process.getFlowElement(); 83 | List flowNodes = flowElements.stream() 84 | .map(JAXBElement::getValue) 85 | .filter(TFlowNode.class::isInstance) 86 | .map(TFlowNode.class::cast) 87 | .collect(Collectors.toList()); 88 | 89 | List sequenceFlows = flowElements.stream() 90 | .map(JAXBElement::getValue) 91 | .filter(TSequenceFlow.class::isInstance) 92 | .map(TSequenceFlow.class::cast) 93 | .collect(Collectors.toList()); 94 | 95 | return new BpmnElements(flowNodes, sequenceFlows); 96 | } 97 | 98 | public void add(TProcess process) { 99 | definitions.getRootElement() 100 | .add(new JAXBElement<>(bpmnElementQName("process"), TProcess.class, null, process)); 101 | } 102 | 103 | public static void addFlowNodes(TProcess process, Collection flowElements) { 104 | flowElements.stream() 105 | .map(f -> new JAXBElement<>(bpmnElementQName(f), TFlowNode.class, null, f)) 106 | .forEach(f -> process.getFlowElement().add(f)); 107 | } 108 | 109 | public static void addSequenceFlows(TProcess process, Collection sequenceFlowElements) { 110 | sequenceFlowElements.stream() 111 | .map(e -> new JAXBElement<>(bpmnElementQName("sequenceFlow"), TSequenceFlow.class, null, e)) 112 | .forEach(e -> process.getFlowElement().add(e)); 113 | } 114 | 115 | private static QName bpmnElementQName(String bpmnElement) { 116 | return new QName("http://www.omg.org/spec/BPMN/20100524/MODEL", bpmnElement, XMLConstants.DEFAULT_NS_PREFIX); 117 | } 118 | 119 | //TODO add other type of flow node elements 120 | private static final Map, String> bpmnElementBindings = new HashMap<>() {{ 121 | put(TParallelGateway.class, "parallelGateway"); 122 | put(TInclusiveGateway.class, "inclusiveGateway"); 123 | put(TExclusiveGateway.class, "exclusiveGateway"); 124 | put(TGateway.class, "gateway"); 125 | put(TTask.class, "task"); 126 | put(TUserTask.class, "userTask"); 127 | put(TServiceTask.class, "serviceTask"); 128 | put(TEndEvent.class, "endEvent"); 129 | put(TStartEvent.class, "startEvent"); 130 | }}; 131 | 132 | private static QName bpmnElementQName(TFlowNode flowNode) { 133 | return bpmnElementQName(bpmnElementBindings.getOrDefault(flowNode.getClass(), "task")); 134 | } 135 | 136 | @RequiredArgsConstructor 137 | @Getter 138 | public static class BpmnElements { 139 | 140 | private final List flowNodes; 141 | private final List sequenceFlows; 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | public class StringUtils { 19 | 20 | public static String defaultIfNull(String string) { 21 | return string != null ? string : ""; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/internal/XmlParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import java.io.StringReader; 19 | import java.io.StringWriter; 20 | 21 | import javax.xml.bind.*; 22 | import javax.xml.transform.stream.StreamSource; 23 | 24 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.ObjectFactory; 25 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.TDefinitions; 26 | 27 | public class XmlParser { 28 | 29 | private static final JAXBContext context = initContext(); 30 | 31 | private static JAXBContext initContext() { 32 | try { 33 | return JAXBContext.newInstance(TDefinitions.class); 34 | } catch (JAXBException e) { 35 | throw new RuntimeException("Unable to initialize the JAXBContext", e); 36 | } 37 | } 38 | 39 | public String marshal(TDefinitions definitions) { 40 | try { 41 | JAXBElement root = new ObjectFactory().createDefinitions(definitions); 42 | StringWriter stringWriter = new StringWriter(); 43 | createMarshaller().marshal(root, stringWriter); 44 | return stringWriter.toString(); 45 | } catch (JAXBException e) { 46 | throw new RuntimeException("Unable to marshal", e); 47 | } 48 | } 49 | 50 | private Marshaller createMarshaller() throws JAXBException { 51 | Marshaller marshaller = context.createMarshaller(); 52 | marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 53 | try { 54 | marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new BpmnNamespacePrefixMapper()); 55 | } catch(PropertyException e) { 56 | // In case another JAXB implementation is used 57 | // do not stop processing, namespace prefixes will be generated automatically in that case 58 | // TODO switch to logger 59 | e.printStackTrace(); 60 | } 61 | return marshaller; 62 | } 63 | 64 | public TDefinitions unmarshall(String xml) { 65 | try { 66 | StreamSource source = new StreamSource(new StringReader(xml)); 67 | JAXBElement root = context.createUnmarshaller().unmarshal(source, TDefinitions.class); 68 | return root.getValue(); 69 | } catch (JAXBException e) { 70 | throw new RuntimeException("Unable to marshal", e); 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/Diagram.java: -------------------------------------------------------------------------------- 1 | package io.process.analytics.tools.bpmn.generator.model; 2 | 3 | import java.util.List; 4 | import java.util.Set; 5 | import java.util.stream.Collectors; 6 | 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.Singular; 10 | 11 | @Data 12 | @Builder 13 | public class Diagram { 14 | 15 | @Singular 16 | private List shapes; 17 | @Singular 18 | private Set edges; 19 | 20 | 21 | public List getIncomingEdges(String shapeId) { 22 | return edges.stream().filter(e -> e.getTo().equals(shapeId)).collect(Collectors.toList()); 23 | } 24 | 25 | public List getOutgoingEdges(String shapeId) { 26 | return edges.stream().filter(e -> e.getFrom().equals(shapeId)).collect(Collectors.toList()); 27 | } 28 | 29 | public List getOriginalIncomingEdges(String shapeId) { 30 | return edges.stream().filter(e -> (e.getTo().equals(shapeId) && !e.isReverted()) || (e.getFrom().equals(shapeId) && e.isReverted())).collect(Collectors.toList()); 31 | } 32 | 33 | public List getOriginalOutgoingEdges(String shapeId) { 34 | return edges.stream().filter(e -> (e.getFrom().equals(shapeId) && !e.isReverted()) || (e.getTo().equals(shapeId) && e.isReverted())).collect(Collectors.toList()); 35 | } 36 | 37 | public Shape getShape(String shapeId) { 38 | return shapes.stream().filter(s -> s.getId().equals(shapeId)).findFirst().get(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/Edge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.model; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.internal.IdUtils.generateRandomId; 19 | 20 | import lombok.Builder; 21 | import lombok.Data; 22 | 23 | @Data 24 | @Builder( toBuilder = true) 25 | public class Edge { 26 | 27 | private final String id; // the bpmnElement id 28 | private final String from; 29 | private final String to; 30 | /** 31 | * This property helps to break cycles 32 | * 33 | * `true` when the original edge is in the opposite direction 34 | * 35 | * When drown, it should be drown in the opposite direction. 36 | * When positioning shapes, it should be kept reverted 37 | */ 38 | private final boolean reverted; 39 | 40 | public static Edge edge(String id, String from, String to) { 41 | return new Edge(id, from, to, false); 42 | } 43 | public static Edge edge(Shape from, Shape to) { 44 | return new Edge(generateRandomId(), copy(from.getId()), copy(to.getId()), false); 45 | } 46 | public static Edge revertedEdge(Edge original) { 47 | return original.toBuilder().from(original.getTo()).to(original.getFrom()).reverted(true).build(); 48 | } 49 | 50 | private static String copy(String s) { 51 | return s + ""; // hack to ensure the returned string is equal to the provided one but doesn't have the same reference 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/Grid.java: -------------------------------------------------------------------------------- 1 | package io.process.analytics.tools.bpmn.generator.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | /** 8 | * 9 | * Represent a grid with coordinate as follows 10 | * 11 | *
 12 |  *  + ➞ x
 13 |  *  ↓
 14 |  *  y
 15 |  * 
16 | */ 17 | // TODO use lombok getter? 18 | public class Grid { 19 | 20 | private final List positions = new ArrayList<>(); 21 | 22 | public static Grid of(Position... positions) { 23 | Grid grid = new Grid(); 24 | for (Position position : positions) { 25 | grid.add(position); 26 | } 27 | return grid; 28 | } 29 | 30 | /** 31 | * @return the width of the grid 32 | */ 33 | public int width() { 34 | return getLastColumnIndex() + 1; 35 | } 36 | 37 | /** 38 | * @return the height of the grid 39 | */ 40 | public int height() { 41 | return getLastRowIndex() + 1; 42 | } 43 | 44 | public Integer getLastRowIndex() { 45 | return positions.stream().map(Position::getY).reduce(-1, Math::max); 46 | } 47 | 48 | public Integer getLastColumnIndex() { 49 | return positions.stream().map(Position::getX).reduce(-1, Math::max); 50 | } 51 | 52 | public List getPositions() { 53 | return positions; 54 | } 55 | 56 | public Position getPosition(String node) { 57 | return positions.stream().filter(position -> position.getShape().equals(node)).findFirst().orElseThrow(() -> new IllegalStateException("Node not yet positionned in grid:" + node)); 58 | } 59 | 60 | public List getRow(int index) { 61 | return positions.stream().filter(p -> p.getY() == index).collect(Collectors.toList()); 62 | } 63 | 64 | public void remove(Position position) { 65 | if (!positions.contains(position)) { 66 | throw new IllegalArgumentException("Position " + position + " is not in the grid"); 67 | } 68 | positions.remove(position); 69 | } 70 | 71 | /** 72 | * Add a new position the grid 73 | * 74 | * @param position to add 75 | */ 76 | public void add(Position position) { 77 | positions.add(position); 78 | } 79 | 80 | /** 81 | * Add a new row after a given row 82 | * This will update positions of all elements already in this grid 83 | * 84 | * @param y add a row after that row 85 | */ 86 | public void addRowAfter(int y) { 87 | for (Position cellToMove : getAllPositionBelow(y)) { 88 | positions.remove(cellToMove); 89 | positions.add(cellToMove.toBuilder().y(cellToMove.getY() + 1).build()); 90 | } 91 | } 92 | 93 | private List getAllPositionBelow(int y) { 94 | return positions.stream().filter(p -> p.getY() > y).collect(Collectors.toList()); 95 | } 96 | 97 | public void addRowBefore(int y) { 98 | addRowAfter(y - 1); 99 | } 100 | 101 | public void removeEmptyRow(int y) { 102 | List row = getRow(y); 103 | if (!row.isEmpty()) { 104 | throw new IllegalArgumentException("Row " + y + " is not empty"); 105 | } 106 | for (Position cellToMove : getAllPositionBelow(y)) { 107 | positions.remove(cellToMove); 108 | positions.add(cellToMove.toBuilder().y(cellToMove.getY() - 1).build()); 109 | } 110 | } 111 | 112 | public boolean isFilled(Position position) { 113 | return positions.stream().anyMatch(p -> p.getX() == position.getX() && p.getY() == position.getY()); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/Position.java: -------------------------------------------------------------------------------- 1 | package io.process.analytics.tools.bpmn.generator.model; 2 | 3 | import lombok.Builder; 4 | import lombok.Data; 5 | 6 | @Data 7 | @Builder(toBuilder = true) 8 | public class Position { 9 | private final String shape; 10 | private final String shapeName; 11 | private final ShapeType shapeType; 12 | private final boolean isSplitGateway; 13 | 14 | private final int x; 15 | private final int y; 16 | 17 | public static Position position(Shape shape, int x, int y) { 18 | return new Position(shape.getId(), shape.getName(), shape.getType(), shape.isSplitGateway(), x, y); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/Shape.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.model; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.model.ShapeType.ACTIVITY; 19 | 20 | import lombok.Builder; 21 | import lombok.Data; 22 | import lombok.RequiredArgsConstructor; 23 | 24 | @Data 25 | @Builder(toBuilder = true) 26 | @RequiredArgsConstructor 27 | // TODO switch to record 28 | public class Shape { 29 | 30 | private final String id; // the bpmnElement id 31 | private final String name; 32 | private final ShapeType type; 33 | private final boolean isSplitGateway; 34 | 35 | public static Shape shape(String name) { 36 | return new Shape(name, name, ACTIVITY, false); 37 | } 38 | 39 | public static Shape shape(String id, String name) { 40 | return new Shape(id, name, ACTIVITY, false); 41 | } 42 | 43 | public static Shape shape(String id, String name, ShapeType type) { 44 | return new Shape(id, name, type, false); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/ShapeType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.model; 17 | 18 | public enum ShapeType { 19 | ACTIVITY, EVENT, GATEWAY 20 | } 21 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/display/DisplayDimension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.model.display; 14 | 15 | import lombok.RequiredArgsConstructor; 16 | 17 | @RequiredArgsConstructor 18 | // TODO switch to record 19 | public class DisplayDimension { 20 | 21 | public final int x; 22 | public final int y; 23 | public final int width; 24 | public final int height; 25 | } 26 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/display/DisplayEdge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.model.display; 14 | 15 | import lombok.Builder; 16 | import lombok.RequiredArgsConstructor; 17 | 18 | import java.util.List; 19 | 20 | @RequiredArgsConstructor 21 | @Builder 22 | // TODO switch to record 23 | public class DisplayEdge { 24 | 25 | public final String bpmnElementId; 26 | public final List wayPoints; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/display/DisplayFlowNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.model.display; 14 | 15 | import io.process.analytics.tools.bpmn.generator.model.ShapeType; 16 | import lombok.Builder; 17 | import lombok.RequiredArgsConstructor; 18 | 19 | @RequiredArgsConstructor 20 | @Builder 21 | // TODO switch to record 22 | public class DisplayFlowNode { 23 | 24 | public final String bpmnElementId; 25 | public final DisplayDimension dimension; 26 | public final DisplayLabel label; 27 | // for non BPMN exporters only 28 | public final ShapeType type; 29 | public final int rx; 30 | public final int strokeWidth; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/display/DisplayLabel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.model.display; 14 | 15 | import lombok.RequiredArgsConstructor; 16 | 17 | @RequiredArgsConstructor 18 | // TODO switch to record 19 | public class DisplayLabel { 20 | 21 | public final String text; // for non BPMN exporters only 22 | public final int fontSize; 23 | public final DisplayDimension dimension; 24 | } 25 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/display/DisplayModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.model.display; 14 | 15 | import lombok.Builder; 16 | import lombok.RequiredArgsConstructor; 17 | import lombok.Singular; 18 | 19 | import java.util.List; 20 | 21 | @RequiredArgsConstructor 22 | @Builder 23 | // TODO switch to record 24 | public class DisplayModel { 25 | public final int width; 26 | public final int height; 27 | 28 | @Singular 29 | public final List flowNodes; 30 | @Singular 31 | public final List edges; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /java/src/main/java/io/process/analytics/tools/bpmn/generator/model/display/DisplayPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.model.display; 14 | 15 | import lombok.RequiredArgsConstructor; 16 | import lombok.ToString; 17 | 18 | @RequiredArgsConstructor 19 | @ToString 20 | // TODO switch to record 21 | public class DisplayPoint { 22 | 23 | public final int x; 24 | public final int y; 25 | } 26 | -------------------------------------------------------------------------------- /java/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /java/src/main/xsd/BPMN20.xsd: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /java/src/main/xsd/BPMNDI.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /java/src/main/xsd/DC.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /java/src/main/xsd/DI.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/AppFromBpmnTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator; 17 | 18 | import org.junit.jupiter.api.Test; 19 | 20 | import java.io.File; 21 | import java.io.IOException; 22 | 23 | import static io.process.analytics.tools.bpmn.generator.App.runApp; 24 | import static io.process.analytics.tools.bpmn.generator.AppTest.*; 25 | import static io.process.analytics.tools.bpmn.generator.internal.FileUtils.fileContent; 26 | import static org.assertj.core.api.Assertions.assertThat; 27 | 28 | public class AppFromBpmnTest { 29 | 30 | @Test 31 | public void main_generates_output_files_for_A_2_0() throws Exception { 32 | runAndCheckBpmnAndSvgGeneration("A.2.0.bpmn.xml"); 33 | } 34 | 35 | @Test 36 | public void main_generates_output_files_for_A_2_1() throws Exception { 37 | runAndCheckBpmnAndSvgGeneration("A.2.1.bpmn.xml"); 38 | } 39 | 40 | @Test 41 | public void main_generates_ascii_output_file() throws Exception { 42 | String outputPath = outputPath("02-startEvent_task_endEvent-without-collaboration.bpmn.txt"); 43 | runApp(input("bpmn/02-startEvent_task_endEvent-without-collaboration.bpmn.xml"), "--output-type=ASCII", "-o", outputPath); 44 | 45 | File asciiFile = new File(outputPath); 46 | assertThat(asciiFile).exists().isFile(); 47 | assertThat(fileContent(asciiFile)).contains("+---"); 48 | } 49 | 50 | @Test 51 | public void main_generates_output_files_for_waypoints_positions_for_gateways() throws Exception { 52 | runAndCheckBpmnAndSvgGeneration("waypoints-positions-gateways.bpmn.xml"); 53 | } 54 | 55 | @Test 56 | public void main_generates_output_files_for_waypoints_positions_for_gateways_split_join() throws Exception { 57 | runAndCheckBpmnAndSvgGeneration("waypoints-positions-gateways_split_join.bpmn.xml"); 58 | } 59 | 60 | @Test 61 | public void main_generates_output_files_for_waypoints_positions_cycle_01_simple() throws Exception { 62 | runAndCheckBpmnAndSvgGeneration("waypoints-positions-cycle_01_simple.bpmn.xml"); 63 | } 64 | 65 | @Test 66 | public void main_generates_output_files_for_waypoints_positions_cycle_02_gateways_in_cycle() throws Exception { 67 | runAndCheckBpmnAndSvgGeneration("waypoints-positions-cycle_02_gateways_in_cycle.bpmn.xml"); 68 | } 69 | 70 | // ================================================================================================================= 71 | // Avoid edge overlap on shape 72 | // ================================================================================================================= 73 | 74 | @Test 75 | public void main_generates_output_files_waypoints_avoid_edge_overlap_01_single_branch() throws Exception { 76 | runAndCheckBpmnAndSvgGeneration("waypoints-avoid-edge-overlap-01-single_branch.bpmn.xml"); 77 | } 78 | 79 | @Test 80 | public void main_generates_output_files_waypoints_avoid_edge_overlap_02_2nd_branch_with_large_height() 81 | throws Exception { 82 | runAndCheckBpmnAndSvgGeneration("waypoints-avoid-edge-overlap-02-2nd-branch-with-large-height.bpmn.xml"); 83 | } 84 | 85 | @Test 86 | public void main_generates_output_files_waypoints_avoid_edge_overlap_03_elements_in_front() 87 | throws Exception { 88 | runAndCheckBpmnAndSvgGeneration("waypoints-avoid-edge-overlap-03-elements_in_front.bpmn.xml"); 89 | } 90 | 91 | @Test 92 | public void main_generates_output_files_waypoints_avoid_edge_overlap_04_multiple_empty_paths() throws Exception { 93 | runAndCheckBpmnAndSvgGeneration("waypoints-avoid-edge-overlap-04-multiple_empty_paths.bpmn.xml"); 94 | } 95 | 96 | // ================================================================================================================= 97 | // UTILS 98 | // ================================================================================================================= 99 | 100 | private static void runAndCheckBpmnAndSvgGeneration(String inputFileName) throws IOException { 101 | runAndCheckBpmnGeneration(inputFileName); 102 | runAndCheckSvgGeneration(inputFileName); 103 | } 104 | 105 | private static void runAndCheckBpmnGeneration(String inputFileName) throws IOException { 106 | String outputPath = outputPath(inputFileName); 107 | runApp(input("bpmn/" + inputFileName), "-o", outputPath); 108 | assertBpmnOutFile(outputPath); 109 | } 110 | 111 | private static void runAndCheckSvgGeneration(String inputFileName) throws IOException { 112 | String outputPath = outputPath(inputFileName + ".svg"); 113 | runApp(input("bpmn/" + inputFileName), "--output-type=SVG", "-o", outputPath); 114 | assertSvgOutFile(outputPath); 115 | } 116 | 117 | private static String outputPath(String fileName) { 118 | return "target/test/output/AppFromBpmnTest/" + fileName; 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/AppFromCsvTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.App.runApp; 19 | import static io.process.analytics.tools.bpmn.generator.AppTest.*; 20 | 21 | import java.io.IOException; 22 | 23 | import org.junit.jupiter.api.Test; 24 | 25 | public class AppFromCsvTest { 26 | 27 | @Test 28 | public void main_generates_bpmn_with_simple_discovery_data() throws Exception { 29 | runAndCheckBpmnAndSvgGeneration("csv/PatientsProcess/nodeSimple.csv", "csv/PatientsProcess/edgeSimple.csv", 30 | "from_simple_csv_diagram.bpmn"); 31 | } 32 | 33 | @Test 34 | public void main_generates_bpmn_with_gateways() throws Exception { 35 | runAndCheckBpmnAndSvgGeneration("csv/PatientsProcess/gateway_node_simple.csv", 36 | "csv/PatientsProcess/gateway_edge_simple.csv", "from_csv_with_gateways.bpmn"); 37 | } 38 | 39 | @Test 40 | public void main_generates_bpmn_with_patients_process_discovery_data() throws Exception { 41 | runAndCheckBpmnAndSvgGeneration("csv/PatientsProcess/node.csv", "csv/PatientsProcess/edge.csv", 42 | "from_patients_csv_diagram.bpmn"); 43 | } 44 | 45 | @Test 46 | public void main_generates_bpmn_with_vacation_request_bonita_discovery_data() throws Exception { 47 | runAndCheckBpmnAndSvgGeneration("csv/VacationRequestBonita/nodes.csv", "csv/VacationRequestBonita/edges.csv", 48 | "from_vacation_request_bonita_csv_diagram.bpmn"); 49 | } 50 | 51 | @Test 52 | public void main_generates_bpmn_with_vacation_request_bonita_v2_discovery_data() throws Exception { 53 | runAndCheckBpmnAndSvgGeneration("csv/VacationRequestBonita_v2/nodes.csv", 54 | "csv/VacationRequestBonita_v2/edges.csv", 55 | "from_vacation_request_bonita_v2_csv_diagram.bpmn"); 56 | } 57 | 58 | // ================================================================================================================= 59 | // UTILS 60 | // ================================================================================================================= 61 | 62 | private static void runAndCheckBpmnAndSvgGeneration(String inputNodesFileName, String inputEdgesFileName, 63 | String outputFileBaseName) throws IOException { 64 | runAndCheckBpmnGeneration(inputNodesFileName, inputEdgesFileName, outputFileBaseName + ".xml"); 65 | runAndCheckSvgGeneration(inputNodesFileName, inputEdgesFileName, outputFileBaseName + ".svg"); 66 | } 67 | 68 | private static void runAndCheckBpmnGeneration(String inputNodesFileName, String inputEdgesFileName, 69 | String outputFileName) throws IOException { 70 | String outputPath = outputPath(outputFileName); 71 | runApp("--input-type=CSV", "--output", outputPath, input(inputNodesFileName), input(inputEdgesFileName)); 72 | assertBpmnOutFile(outputPath); 73 | } 74 | 75 | private static void runAndCheckSvgGeneration(String inputNodesFileName, String inputEdgesFileName, String outputFileName) throws IOException { 76 | String outputPath = outputPath(outputFileName); 77 | runApp("--input-type=CSV", "--output-type=SVG", "--output", outputPath, input(inputNodesFileName), input(inputEdgesFileName)); 78 | assertSvgOutFile(outputPath); 79 | } 80 | 81 | private static String outputPath(String fileName) { 82 | return "target/test/output/AppFromCsvTest/" + fileName; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/AppTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.App.runApp; 19 | import static io.process.analytics.tools.bpmn.generator.internal.FileUtils.fileContent; 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | 25 | import org.junit.jupiter.api.Test; 26 | 27 | public class AppTest { 28 | 29 | @Test 30 | public void main_fail_on_non_existing_input_file() throws Exception { 31 | int returnCode = runApp("input"); 32 | 33 | assertThat(returnCode).isEqualTo(2); 34 | } 35 | 36 | @Test 37 | public void main_fail_on_wrong_export_type() throws Exception { 38 | int returnCode = runApp("input", "--output-type", "unknown_export_type"); 39 | 40 | assertThat(returnCode).isEqualTo(2); 41 | } 42 | 43 | // ================================================================================================================= 44 | // UTILS 45 | // ================================================================================================================= 46 | 47 | public static void assertBpmnOutFile(String outputPath) throws IOException { 48 | File bpmnFile = new File(outputPath); 49 | assertThat(bpmnFile).exists().isFile(); 50 | assertThat(fileContent(bpmnFile)).contains(" 34 | // task_1 35 | // 36 | // 37 | // startEvent_1 38 | // endEvent_1 39 | // 40 | // 41 | // task_1 42 | // 43 | // 44 | // 45 | TDefinitions definitions = definitionsFromBpmnFile( 46 | "src/test/resources/bpmn/02-startEvent_task_endEvent-without-collaboration.bpmn.xml"); 47 | 48 | Diagram diagram = new BpmnToAlgoModelConverter().toAlgoModel(definitions); 49 | 50 | assertThat(diagram.getShapes()).containsOnly( 51 | shape("startEvent_1", "Start Event", EVENT), 52 | shape("task_1", "Task 1", ACTIVITY), 53 | shape("endEvent_1", "End Event", EVENT)); 54 | assertThat(diagram.getEdges()).containsOnly( 55 | edge("sequenceFlow_1", "startEvent_1", "task_1"), 56 | edge("sequenceFlow_2", "task_1", "endEvent_1")); 57 | } 58 | 59 | @Test 60 | void should_convert_activities_to_shape() { 61 | assertThat(toShape(new TUserTask(), new TSendTask(), new TSubProcess())) 62 | .containsOnly(shape("bpmn_id", "bpmn_name", ACTIVITY)); 63 | } 64 | 65 | private static Stream toShape(TFlowNode... flowNodes) { 66 | return Stream.of(flowNodes).map(BpmnToAlgoModelConverterTest::setBaseFields) 67 | .map(BpmnToAlgoModelConverter::toShape); 68 | } 69 | 70 | private static TFlowNode setBaseFields(TFlowNode flowNode) { 71 | flowNode.setId("bpmn_id"); 72 | flowNode.setName("bpmn_name"); 73 | return flowNode; 74 | } 75 | 76 | @Test 77 | void should_convert_events_to_shape() { 78 | assertThat(toShape(new TStartEvent(), new TIntermediateCatchEvent(), new TEndEvent())) 79 | .containsOnly(shape("bpmn_id", "bpmn_name", EVENT)); 80 | } 81 | 82 | @Test 83 | void should_convert_gateways_to_shape() { 84 | assertThat(toShape(new TParallelGateway(), new TComplexGateway())) 85 | .containsOnly(shape("bpmn_id", "bpmn_name", GATEWAY)); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/export/SVGExporterTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | package io.process.analytics.tools.bpmn.generator.export; 15 | 16 | import io.process.analytics.tools.bpmn.generator.converter.waypoint.Direction; 17 | import io.process.analytics.tools.bpmn.generator.model.display.DisplayPoint; 18 | import org.junit.jupiter.params.ParameterizedTest; 19 | import org.junit.jupiter.params.provider.Arguments; 20 | import org.junit.jupiter.params.provider.MethodSource; 21 | 22 | import java.util.List; 23 | import java.util.stream.Stream; 24 | 25 | import static org.assertj.core.api.Assertions.assertThat; 26 | 27 | class SVGExporterTest { 28 | 29 | @ParameterizedTest 30 | @MethodSource("provideWaypointsForDirectionDetection") 31 | void detect_last_segment_direction(List waypoints, Direction expectedDirection) { 32 | var direction = SVGExporter.detectLastSegmentDirection(waypoints); 33 | assertThat(direction).isEqualTo(expectedDirection); 34 | } 35 | 36 | private static Stream provideWaypointsForDirectionDetection() { 37 | return Stream.of( 38 | Arguments.of(List.of(new DisplayPoint(0, 37), new DisplayPoint(10, 37)), Direction.LeftToRight), 39 | Arguments.of(List.of(new DisplayPoint(40, 58), new DisplayPoint(10, 58)), Direction.RightToLeft), 40 | Arguments.of(List.of(new DisplayPoint(13, 73), new DisplayPoint(13, 137)), Direction.TopToBottom), 41 | Arguments.of(List.of(new DisplayPoint(33, 73), new DisplayPoint(33, 37)), Direction.BottomToTop) 42 | ); 43 | } 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/input/CSVtoBPMNTest.java: -------------------------------------------------------------------------------- 1 | package io.process.analytics.tools.bpmn.generator.input; 2 | 3 | import io.process.analytics.tools.bpmn.generator.internal.Semantic; 4 | import io.process.analytics.tools.bpmn.generator.internal.Semantic.BpmnElements; 5 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.*; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import javax.xml.namespace.QName; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | import static io.process.analytics.tools.bpmn.generator.internal.FileUtils.fileContent; 15 | import static io.process.analytics.tools.bpmn.generator.internal.Semantic.getId; 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | class CSVtoBPMNTest { 19 | @Test 20 | public void should_convert_csv_to_bpmn() throws IOException { 21 | String edge = readCsvFile("src/test/resources/csv/PatientsProcess/edge.csv"); 22 | String node = readCsvFile("src/test/resources/csv/PatientsProcess/node.csv"); 23 | 24 | TDefinitions definitions = new CSVtoBPMN().readFromCSV(node, edge); 25 | assertThat(definitions.getId()).isNotNull(); 26 | Semantic semantic = new Semantic(definitions); 27 | 28 | List processes = semantic.getProcesses(); 29 | assertThat(processes).hasSize(1); 30 | TProcess process = processes.get(0); 31 | assertThat(process.getId()).isNotNull(); 32 | BpmnElements bpmnElements = semantic.getBpmnElements(process); 33 | List flowNodes = bpmnElements.getFlowNodes(); 34 | assertThat(flowNodes).hasSize(9); 35 | TFlowElement flowElement0 = flowNodes.get(0); 36 | assertThat(flowElement0.getId()).isEqualTo("bpmnElement_1"); 37 | assertThat(flowElement0.getName()).isEqualTo("End"); 38 | assertThat(flowElement0).isExactlyInstanceOf(TTask.class); 39 | 40 | List sequenceFlows = bpmnElements.getSequenceFlows(); 41 | assertThat(sequenceFlows).hasSize(13); 42 | TSequenceFlow sequenceFlow12 = sequenceFlows.get(12); 43 | assertThat(sequenceFlow12.getId()).isEqualTo("sequenceFlow_13"); 44 | assertThat(getId(sequenceFlow12.getSourceRef())).isEqualTo("bpmnElement_9"); 45 | assertThat(getId(sequenceFlow12.getTargetRef())).isEqualTo("bpmnElement_5"); 46 | } 47 | 48 | @Test 49 | public void should_convert_csv_with_gateways() throws IOException { 50 | String edge = readCsvFile("src/test/resources/csv/PatientsProcess/gateway_edge_simple.csv"); 51 | String node = readCsvFile("src/test/resources/csv/PatientsProcess/gateway_node_simple.csv"); 52 | 53 | TDefinitions definitions = new CSVtoBPMN().readFromCSV(node, edge); 54 | 55 | Semantic semantic = new Semantic(definitions); 56 | BpmnElements bpmnElements = semantic.getBpmnElements(semantic.getProcesses().get(0)); 57 | List flowNodes = bpmnElements.getFlowNodes(); 58 | assertThat(flowNodes).hasSize(5); 59 | assertThat(flowNodes).anyMatch(f -> f.getName().equals("End") && (f instanceof TEndEvent)); 60 | assertThat(flowNodes).anyMatch(f -> f.getName().equals("Start") && (f instanceof TStartEvent)); 61 | assertThat(flowNodes).anyMatch(f -> f.getName().equals("Task1") && (f instanceof TTask)); 62 | assertThat(flowNodes).anyMatch(f -> f.getName().equals("UserTask") && (f instanceof TUserTask)); 63 | assertThat(flowNodes).anyMatch(f -> f.getName().equals("Gateway1") && (f instanceof TGateway)); 64 | } 65 | 66 | @Test 67 | public void should_set_incoming_and_outgoing_edges() throws IOException { 68 | String edge = readCsvFile("src/test/resources/csv/PatientsProcess/gateway_edge_simple.csv"); 69 | String node = readCsvFile("src/test/resources/csv/PatientsProcess/gateway_node_simple.csv"); 70 | 71 | TDefinitions definitions = new CSVtoBPMN().readFromCSV(node, edge); 72 | 73 | Semantic semantic = new Semantic(definitions); 74 | BpmnElements bpmnElements = semantic.getBpmnElements(semantic.getProcesses().get(0)); 75 | List flowNodes = bpmnElements.getFlowNodes(); 76 | TGateway gateway1 = (TGateway) flowNodes.stream().filter(f -> f.getName().equals("Gateway1")) 77 | .collect(Collectors.toList()).get(0); 78 | 79 | assertThat(gateway1.getIncoming()).extracting(QName::getLocalPart).containsExactlyInAnyOrder("sequenceFlow_1"); 80 | assertThat(gateway1.getOutgoing()).extracting(QName::getLocalPart).containsExactlyInAnyOrder("sequenceFlow_2", "sequenceFlow_3"); 81 | } 82 | 83 | // ================================================================================================================= 84 | // UTILS 85 | // ================================================================================================================= 86 | 87 | // TODO the underlying method read lines then join. The CSVtoBPMN spit the string into lines! Useless work! 88 | private static String readCsvFile(String csvFilePath) throws IOException { 89 | return fileContent(new File(csvFilePath)); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/internal/BPMNDiagramRichBuilderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.internal.SemanticTest.definitionsFromBpmnFile; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import java.util.List; 22 | 23 | import javax.xml.namespace.QName; 24 | 25 | import org.junit.jupiter.api.Test; 26 | 27 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.BPMNDiagram; 28 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.BPMNPlane; 29 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.TDefinitions; 30 | 31 | class BPMNDiagramRichBuilderTest { 32 | 33 | @Test 34 | public void initializeBPMNDiagram_when_collaboration_exist_in_bpmn_file() { 35 | BPMNDiagramRichBuilder builder = newBuildFromBpmnFile("src/test/resources/bpmn/01-startEvent.bpmn.xml"); 36 | 37 | BPMNDiagram diagram = builder.initializeBPMNDiagram(); 38 | 39 | BPMNPlane plane = check(diagram); 40 | checkBpmnElement(plane, "Collaboration_1cajy2f"); 41 | } 42 | 43 | @Test 44 | public void initializeBPMNDiagram_when_collaboration_not_exist_in_bpmn_file() { 45 | BPMNDiagramRichBuilder builder = newBuildFromBpmnFile("src/test/resources/bpmn/02-startEvent_task_endEvent-without-collaboration.bpmn.xml"); 46 | 47 | BPMNDiagram diagram = builder.initializeBPMNDiagram(); 48 | 49 | BPMNPlane plane = check(diagram); 50 | checkBpmnElement(plane, "process_1"); 51 | } 52 | 53 | @Test 54 | public void build_should_initialize_BPMNDiagram() { 55 | BPMNDiagramRichBuilder builder = newBuildFromBpmnFile("src/test/resources/bpmn/A.2.0.bpmn.xml"); 56 | 57 | TDefinitions definitions = builder.build(); 58 | 59 | // TODO duplication with XmlParserTest + check method for id 60 | // Shapes 61 | List diagram = definitions.getBPMNDiagram(); 62 | assertThat(diagram) 63 | .hasSize(1) 64 | .extracting(BPMNDiagram::getId).containsOnly("BPMNDiagram_1"); 65 | BPMNPlane plane = diagram.get(0).getBPMNPlane(); 66 | assertThat(plane.getId()).isEqualTo("BPMNPlane_1"); 67 | } 68 | 69 | // ================================================================================================================= 70 | // UTILS 71 | // ================================================================================================================= 72 | 73 | // always set like this 74 | private static BPMNPlane check(BPMNDiagram bpmnDiagram) { 75 | assertThat(bpmnDiagram.getId()).isEqualTo("BPMNDiagram_1"); 76 | BPMNPlane bpmnPlane = bpmnDiagram.getBPMNPlane(); 77 | assertThat(bpmnPlane.getId()).isEqualTo("BPMNPlane_1"); 78 | return bpmnPlane; 79 | } 80 | 81 | private static void checkBpmnElement(BPMNPlane plane, String expectedId) { 82 | QName bpmnElement = plane.getBpmnElement(); 83 | assertThat(bpmnElement.getLocalPart()).isEqualTo(expectedId); 84 | assertThat(bpmnElement.getNamespaceURI()).isEmpty(); 85 | assertThat(bpmnElement.getPrefix()).isEmpty(); 86 | } 87 | 88 | private static BPMNDiagramRichBuilder newBuildFromBpmnFile(String bpmnFilePath) { 89 | return new BPMNDiagramRichBuilder(definitionsFromBpmnFile(bpmnFilePath)); 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/internal/SemanticTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific language governing permissions and 11 | * limitations under the License. 12 | */ 13 | package io.process.analytics.tools.bpmn.generator.internal; 14 | 15 | import static io.process.analytics.tools.bpmn.generator.internal.BpmnInOut.defaultBpmnInOut; 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | import static org.assertj.core.api.Assertions.tuple; 18 | 19 | import java.io.File; 20 | 21 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.*; 22 | import org.junit.jupiter.api.Test; 23 | 24 | public class SemanticTest { 25 | 26 | @Test 27 | public void detect_collaboration_when_exist_in_bpmn_file() { 28 | Semantic semantic = semanticFromBpmnFile("src/test/resources/bpmn/01-startEvent.bpmn.xml"); 29 | assertThat(semantic.getCollaboration()) 30 | .get() 31 | .hasFieldOrPropertyWithValue("id", "Collaboration_1cajy2f"); 32 | } 33 | 34 | @Test 35 | public void detect_collaboration_when_not_exist_in_bpmn_file() { 36 | Semantic semantic = semanticFromBpmnFile( 37 | "src/test/resources/bpmn/02-startEvent_task_endEvent-without-collaboration.bpmn.xml"); 38 | assertThat(semantic.getCollaboration()).isEmpty(); 39 | } 40 | 41 | @Test 42 | public void detect_processes_in_bpmn_file() { 43 | Semantic semantic = semanticFromBpmnFile("src/test/resources/bpmn/01-startEvent.bpmn.xml"); 44 | assertThat(semantic.getProcesses()) 45 | .hasSize(1) 46 | .extracting(TProcess::getId).containsExactly("Process_1duwsyj"); 47 | } 48 | 49 | @Test 50 | public void detect_bpmn_elements_in_bpmn_file() { 51 | Semantic semantic = semanticFromBpmnFile( 52 | "src/test/resources/bpmn/02-startEvent_task_endEvent-without-collaboration.bpmn.xml"); 53 | // we know that there is a process (see other tests) 54 | TProcess process = semantic.getProcesses().get(0); 55 | 56 | Semantic.BpmnElements bpmnElements = semantic.getBpmnElements(process); 57 | 58 | assertThat(bpmnElements.getFlowNodes()) 59 | .extracting(Object::getClass, TFlowElement::getId) 60 | .containsOnly(tuple(TStartEvent.class, "startEvent_1"), 61 | tuple(TTask.class, "task_1"), 62 | tuple(TEndEvent.class, "endEvent_1")); 63 | assertThat(bpmnElements.getSequenceFlows()) 64 | .extracting(Object::getClass, TFlowElement::getId) 65 | .containsOnly(tuple(TSequenceFlow.class, "sequenceFlow_1"), 66 | tuple(TSequenceFlow.class, "sequenceFlow_2")); 67 | } 68 | 69 | public static TDefinitions definitionsFromBpmnFile(String bpmnFilePath) { 70 | return defaultBpmnInOut().readFromBpmn(new File(bpmnFilePath)); 71 | } 72 | 73 | private static Semantic semanticFromBpmnFile(String bpmnFilePath) { 74 | return new Semantic(definitionsFromBpmnFile(bpmnFilePath)); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/internal/XmlParserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.internal; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.internal.FileUtils.fileContent; 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import java.io.File; 22 | import java.io.IOException; 23 | import java.util.List; 24 | 25 | import javax.xml.bind.JAXBElement; 26 | 27 | import org.junit.jupiter.api.Test; 28 | 29 | import io.process.analytics.tools.bpmn.generator.internal.generated.model.*; 30 | 31 | public class XmlParserTest { 32 | 33 | private final XmlParser xmlParser = new XmlParser(); 34 | 35 | @Test 36 | public void unmarshall() throws IOException { 37 | String bpmnAsXml = fileContent(new File("src/test/resources/bpmn/01-startEvent.bpmn.xml")); 38 | 39 | TDefinitions definitions = xmlParser.unmarshall(bpmnAsXml); 40 | 41 | // Semantic 42 | List> rootElement = definitions.getRootElement(); 43 | assertThat(rootElement) 44 | .hasSize(2) 45 | .extracting(JAXBElement::getValue) 46 | .extracting(TRootElement::getClass) 47 | .extracting(Class::getName) 48 | .containsExactly(TCollaboration.class.getName(),TProcess.class.getName()); 49 | 50 | // Shapes 51 | List diagram = definitions.getBPMNDiagram(); 52 | assertThat(diagram) 53 | .hasSize(1) 54 | .extracting(BPMNDiagram::getId).containsOnly("BPMNDiagram_1"); 55 | BPMNPlane plane = diagram.get(0).getBPMNPlane(); 56 | assertThat(plane.getId()).isEqualTo("BPMNPlane_1"); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /java/src/test/java/io/process/analytics/tools/bpmn/generator/model/GridTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 Bonitasoft S.A. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.process.analytics.tools.bpmn.generator.model; 17 | 18 | import static io.process.analytics.tools.bpmn.generator.model.Position.position; 19 | import static io.process.analytics.tools.bpmn.generator.model.Shape.shape; 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | 22 | import org.junit.jupiter.api.Test; 23 | 24 | class GridTest { 25 | 26 | private final Grid grid = new Grid(); 27 | private final Shape nodeA = shape("a"); 28 | private final Shape nodeB = shape("b"); 29 | private final Shape nodeC = shape("c"); 30 | 31 | @Test 32 | public void should_move_element_when_adding_row_after() { 33 | grid.add(position(nodeA, 0, 0)); 34 | grid.add(position(nodeB, 0, 1)); 35 | grid.add(position(nodeC, 0, 2)); 36 | 37 | 38 | grid.addRowAfter(1); 39 | 40 | assertThat(grid.getPositions()).containsExactly( 41 | position(nodeA, 0, 0), 42 | position(nodeB, 0, 1), 43 | position(nodeC, 0, 3) 44 | ); 45 | } 46 | @Test 47 | public void should_move_element_when_adding_row_before() { 48 | grid.add(position(nodeA, 0, 0)); 49 | grid.add(position(nodeB, 0, 1)); 50 | grid.add(position(nodeC, 0, 2)); 51 | 52 | 53 | grid.addRowBefore(1); 54 | 55 | assertThat(grid.getPositions()).containsExactly( 56 | position(nodeA, 0, 0), 57 | position(nodeB, 0, 2), 58 | position(nodeC, 0, 3) 59 | ); 60 | } 61 | 62 | @Test 63 | public void should_not_move_element_when_adding_row_after_all_existing_elements() { 64 | grid.add(position(nodeA, 0, 0)); 65 | grid.add(position(nodeB, 0, 1)); 66 | grid.add(position(nodeC, 0, 2)); 67 | 68 | 69 | grid.addRowAfter(2); 70 | 71 | assertThat(grid.getPositions()).containsExactly( 72 | position(nodeA, 0, 0), 73 | position(nodeB, 0, 1), 74 | position(nodeC, 0, 2) 75 | ); 76 | } 77 | 78 | } -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/01-startEvent.bpmn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Flow_1bic2fy 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/02-startEvent_task_endEvent-without-collaboration.bpmn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | task_1 6 | 7 | 8 | startEvent_1 9 | endEvent_1 10 | 11 | 12 | task_1 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-01-single_branch.bpmn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-01-single_branch.bpmn.png -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-01-single_branch.bpmn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_1vicax8 6 | 7 | 8 | Flow_1vicax8 9 | Flow_0h6tbri 10 | Flow_0uof6cs 11 | 12 | 13 | 14 | Flow_0h6tbri 15 | Flow_1ayzxfr 16 | 17 | 18 | 19 | Flow_1ayzxfr 20 | Flow_1g2c8c6 21 | 22 | 23 | 24 | Flow_1g2c8c6 25 | Flow_0uof6cs 26 | Flow_1oneadm 27 | 28 | 29 | 30 | 31 | Flow_1oneadm 32 | Flow_10qs96y 33 | 34 | 35 | 36 | Flow_10qs96y 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-02-2nd-branch-with-large-height.bpmn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-02-2nd-branch-with-large-height.bpmn.png -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-03-elements_in_front.bpmn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-03-elements_in_front.bpmn.png -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-04-multiple_empty_paths.bpmn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-avoid-edge-overlap-04-multiple_empty_paths.bpmn.png -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-positions-cycle_01_simple.bpmn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-positions-cycle_01_simple.bpmn.png -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-positions-cycle_01_simple.bpmn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Flow_103eu6e 6 | 7 | 8 | Flow_103eu6e 9 | Flow_0c5bz8z 10 | Flow_1rmk1d7 11 | 12 | 13 | 14 | Flow_1rmk1d7 15 | Flow_1699vu8 16 | 17 | 18 | 19 | Flow_1699vu8 20 | Flow_16flag6 21 | Flow_0f6ssax 22 | 23 | 24 | 25 | Flow_16flag6 26 | 27 | 28 | 29 | Flow_0w1nknb 30 | Flow_0c5bz8z 31 | 32 | 33 | 34 | Flow_0f6ssax 35 | Flow_0w1nknb 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-positions-cycle_02_gateways_in_cycle.bpmn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-positions-cycle_02_gateways_in_cycle.bpmn.png -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-positions-gateways.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-positions-gateways.png -------------------------------------------------------------------------------- /java/src/test/resources/bpmn/waypoints-positions-gateways_split_join.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/process-analytics/bpmn-layout-generators/6bea69c8bdd1936eca949eb2ad8f40dc2985d831/java/src/test/resources/bpmn/waypoints-positions-gateways_split_join.png -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/edge.csv: -------------------------------------------------------------------------------- 1 | "","id","from","to" 2 | "1",1,2,7 3 | "2",2,3,1 4 | "3",3,3,6 5 | "4",4,4,1 6 | "5",5,5,1 7 | "6",6,5,4 8 | "7",7,6,5 9 | "8",8,7,8 10 | "9",9,8,1 11 | "10",10,8,3 12 | "11",11,8,9 13 | "12",12,9,1 14 | "13",13,9,5 15 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/edgeSimple.csv: -------------------------------------------------------------------------------- 1 | "","id","from","to" 2 | "1",1,2,3 3 | "2",2,3,1 4 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/gateway_edge_simple.csv: -------------------------------------------------------------------------------- 1 | "","id","from","to" 2 | "1",1,2,5 3 | "2",2,5,3 4 | "3",3,5,4 5 | "4",4,3,1 6 | "5",5,4,1 7 | 8 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/gateway_node_simple.csv: -------------------------------------------------------------------------------- 1 | "","id","label","type" 2 | "1",1,"End","end_event" 3 | "2",2,"Start","start_event" 4 | "3",3,"Task1","task" 5 | "4",4,"UserTask","user_task" 6 | "5",5,"Gateway1","gateway" 7 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/gateways_edge.csv: -------------------------------------------------------------------------------- 1 | "","id","from_id","to_id" 2 | "1",1,2,7 3 | "2",2,4,1 4 | "3",3,6,5 5 | "4",4,7,8 6 | "5",5,3,1003 7 | "6",6,5,1005 8 | "7",7,8,1008 9 | "8",8,9,1009 10 | "9",9,1003,1 11 | "10",10,1003,6 12 | "11",11,1005,1 13 | "12",12,1005,4 14 | "13",13,1008,1 15 | "14",14,1008,3 16 | "15",15,1008,9 17 | "16",16,1009,1 18 | "17",17,1009,5 -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/gateways_node.csv: -------------------------------------------------------------------------------- 1 | "","from_id","node","type" 2 | "1",1,"ARTIFICIAL_END","task" 3 | "2",2,"ARTIFICIAL_START","task" 4 | "3",3,"Blood test","task" 5 | "4",4,"Check-out","task" 6 | "5",5,"Discuss Results","task" 7 | "6",6,"MRI SCAN","task" 8 | "7",7,"Registration","task" 9 | "8",8,"Triage and Assessment","task" 10 | "9",9,"X-Ray","task" 11 | "21",1003,"gateways1","gateway" 12 | "41",1005,"gateways2","gateway" 13 | "61",1008,"gateways3","gateway" 14 | "91",1009,"gateways4","gateway" 15 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/node.csv: -------------------------------------------------------------------------------- 1 | "","id","label","type" 2 | "1",1,"End","task" 3 | "2",2,"Start","task" 4 | "3",3,"Blood test 237","task" 5 | "4",4,"Check-out 492","task" 6 | "5",5,"Discuss Results 495","task" 7 | "6",6,"MRI SCAN 236","task" 8 | "7",7,"Registration 500","task" 9 | "8",8,"Triage and Assessment 500","task" 10 | "9",9,"X-Ray 261","task" 11 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/PatientsProcess/nodeSimple.csv: -------------------------------------------------------------------------------- 1 | "","id","label","type" 2 | "1",1,"End","task" 3 | "2",2,"Start","task" 4 | "3",3,"Blood test 237","task" 5 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/VacationRequestBonita/edges.csv: -------------------------------------------------------------------------------- 1 | "","id","from_id","to_id","value" 2 | "1",1,1,2004,52 3 | "2",2,3,9,53 4 | "3",3,7,2008,3 5 | "4",4,9,10,53 6 | "5",5,10,2014,53 7 | "6",6,11,6,53 8 | "7",7,12,3,53 9 | "8",8,13,2,53 10 | "9",9,15,7,3 11 | "10",10,2,1002,1 12 | "11",11,5,1005,2 13 | "12",12,6,1006,50 14 | "13",13,8,1008,1 15 | "14",14,14,1014,2 16 | "15",15,1002,1,52 17 | "16",16,1002,2004,1 18 | "17",17,1005,11,53 19 | "18",18,1005,2008,2 20 | "19",19,1005,2012,1 21 | "20",20,1006,2015,1 22 | "21",21,1006,2008,50 23 | "22",22,1006,2012,2 24 | "23",23,1008,2014,2 25 | "24",24,1008,2008,1 26 | "25",25,1014,2014,1 27 | "26",26,1014,2015,2 28 | "27",27,1014,13,53 29 | "28",28,2004,12,1 30 | "29",29,2008,14,50 31 | "30",30,2012,15,2 32 | "31",31,2014,4,53 33 | "32",32,2015,8,2 34 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/VacationRequestBonita/nodes.csv: -------------------------------------------------------------------------------- 1 | "","from_id","node","type","value" 2 | "1",1,"Add event on employee calendar","task",52 3 | "2",2,"after approval branch","task",53 4 | "3",3,"after approval join","task",53 5 | "4",4,"ARTIFICIAL_END","end_event",56 6 | "5",5,"ARTIFICIAL_START","start_event",56 7 | "6",6,"Deduct requested days from available days","task",53 8 | "7",7,"End Reminder","task",3 9 | "8",8,"end when request cancelled","task",3 10 | "9",9,"Gateway1","task",53 11 | "10",10,"New Vacation Request End","task",53 12 | "11",11,"New Vacation Request Start","task",53 13 | "12",12,"Notify employee request approved","task",53 14 | "13",13,"Request approved?","task",53 15 | "14",14,"Review request","task",56 16 | "15",15,"Send reminder","task",3 17 | "16",1002,"gateway1","gateway",53 18 | "17",1005,"gateway2","gateway",56 19 | "18",1006,"gateway3","gateway",53 20 | "19",1008,"gateway4","gateway",3 21 | "20",1014,"gateway5","gateway",56 22 | "21",2004,"gateways_merge_1","gateway",56 23 | "22",2008,"gateways_merge_2","gateway",3 24 | "23",2012,"gateways_merge_3","gateway",53 25 | "24",2014,"gateways_merge_4","gateway",56 26 | "25",2015,"gateways_merge_5","gateway",3 27 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/VacationRequestBonita_v2/edges.csv: -------------------------------------------------------------------------------- 1 | "","id","from_id","to_id","value" 2 | "1",1,1,2009,52 3 | "2",2,5,2010,3 4 | "3",3,7,2002,53 5 | "4",4,8,4,53 6 | "5",5,9,7,53 7 | "6",6,11,5,3 8 | "7",7,3,1003,56 9 | "8",8,4,1004,53 10 | "9",9,6,1006,3 11 | "10",10,10,1010,56 12 | "11",11,1003,8,53 13 | "12",12,1003,2010,2 14 | "13",13,1003,2011,1 15 | "14",14,1004,2006,1 16 | "15",15,1004,2010,50 17 | "16",16,1004,2011,2 18 | "17",17,1006,2002,2 19 | "18",18,1006,2010,1 20 | "19",19,1010,1,52 21 | "20",20,1010,2002,1 22 | "21",21,1010,2006,2 23 | "22",22,1010,2009,1 24 | "23",23,2002,2,56 25 | "24",24,2006,6,3 26 | "25",25,2009,9,53 27 | "26",26,2010,10,56 28 | "27",27,2011,11,3 29 | -------------------------------------------------------------------------------- /java/src/test/resources/csv/VacationRequestBonita_v2/nodes.csv: -------------------------------------------------------------------------------- 1 | "","from_id","node","type","value" 2 | "1",1,"Add event on employee calendar","task",52 3 | "2",2,"ARTIFICIAL_END","end_event",56 4 | "3",3,"ARTIFICIAL_START","start_event",56 5 | "4",4,"Deduct requested days from available days","task",53 6 | "5",5,"End Reminder","task",3 7 | "6",6,"end when request cancelled","task",3 8 | "7",7,"New Vacation Request End","task",53 9 | "8",8,"New Vacation Request Start","task",53 10 | "9",9,"Notify employee request approved","task",53 11 | "10",10,"Review request","task",56 12 | "11",11,"Send reminder","task",3 13 | "12",1003,"gateway1","gateway",56 14 | "13",1004,"gateway2","gateway",53 15 | "14",1006,"gateway3","gateway",3 16 | "15",1010,"gateway4","gateway",56 17 | "16",2002,"gateways_merge_1","gateway",56 18 | "17",2006,"gateways_merge_2","gateway",3 19 | "18",2009,"gateways_merge_3","gateway",53 20 | "19",2010,"gateways_merge_4","gateway",56 21 | "20",2011,"gateways_merge_5","gateway",3 22 | -------------------------------------------------------------------------------- /java/src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | --------------------------------------------------------------------------------