diff --git a/pom.xml b/pom.xml
index 055ee6d556..5f4e722675 100644
--- a/pom.xml
+++ b/pom.xml
@@ -96,7 +96,13 @@
1.9.20.1
- 0.3.0
+
+ 0.4.0-taskana-patch
+
+ 11.4.94.0.14.0.34.0.3
@@ -352,6 +358,7 @@
truewarningtrue
+ **/org/camunda/bpm/dmn/**/*
diff --git a/routing/dmn-xlsx-converter/LEGAL_NOTICE.md b/routing/dmn-xlsx-converter/LEGAL_NOTICE.md
new file mode 100644
index 0000000000..85818394f9
--- /dev/null
+++ b/routing/dmn-xlsx-converter/LEGAL_NOTICE.md
@@ -0,0 +1,16 @@
+This Module is a clone of Projekt Module
+[camunda-dmn-xlsx:xlsx-dmn-converter](https://github.com/camunda-community-hub/camunda-dmn-xlsx/tree/master/xlsx-dmn-converter)
+
+The original project is licensed under APACHE LICENSE Version 2.
+
+All changes to the code are included in this PR: https://github.com/camunda-community-hub/camunda-dmn-xlsx/pull/53
+
+The changes were necessary so that there is a version compatible with jakarta-xml-bind and this version can be used with Camunda.
+
+The main changes were:
+
+* Upgrade of lib `org.docx4j:docx4j:6.0.1` to `org.docx4j:docx4j-JAXB-ReferenceImpl:11.4.9`
+* In class `org.camunda.bpm.dmn.xlsx.XlsxConverter` and `org.camunda.bpm.dmn.xlsx.XlsxWorksheetConverter` a new parameter was introduced: `historyTimeToLive`
+ * because camunda-engine with version 7.20.X requires a timeToLive for history of decisions
+ * default timeToLive is 180 days
+ * the new parameter is set during execution of convert method to the created decisons
diff --git a/routing/dmn-xlsx-converter/LICENSE b/routing/dmn-xlsx-converter/LICENSE
new file mode 100644
index 0000000000..7a4a3ea242
--- /dev/null
+++ b/routing/dmn-xlsx-converter/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/routing/dmn-xlsx-converter/README.md b/routing/dmn-xlsx-converter/README.md
new file mode 100644
index 0000000000..2138fa8e16
--- /dev/null
+++ b/routing/dmn-xlsx-converter/README.md
@@ -0,0 +1,16 @@
+This Module is a clone of Projekt Module
+[camunda-dmn-xlsx:xlsx-dmn-converter](https://github.com/camunda-community-hub/camunda-dmn-xlsx/tree/master/xlsx-dmn-converter)
+
+The original project is licensed under APACHE LICENSE Version 2.
+
+All changes to the code are included in this PR: https://github.com/camunda-community-hub/camunda-dmn-xlsx/pull/53
+
+The changes were necessary so that there is a version compatible with jakarta-xml-bind and this version can be used with Camunda.
+
+The main changes were:
+
+* Upgrade of lib `org.docx4j:docx4j:6.0.1` to `org.docx4j:docx4j-JAXB-ReferenceImpl:11.4.9`
+* In class `org.camunda.bpm.dmn.xlsx.XlsxConverter` and `org.camunda.bpm.dmn.xlsx.XlsxWorksheetConverter` a new parameter was introduced: `historyTimeToLive`
+ * because camunda-engine with version 7.20.X requires a timeToLive for history of decisions
+ * default timeToLive is 180 days
+ * the new parameter is set during execution of convert method to the created decisons
diff --git a/routing/dmn-xlsx-converter/pom.xml b/routing/dmn-xlsx-converter/pom.xml
new file mode 100644
index 0000000000..1afd1c1b75
--- /dev/null
+++ b/routing/dmn-xlsx-converter/pom.xml
@@ -0,0 +1,118 @@
+
+
+ 4.0.0
+ org.camunda.bpm.extension.dmn
+ dmn-xlsx-converter
+ ${version.dmn-xlsx-converter}
+ DMN-XLSX Converter
+ jar
+
+
+ pro.taskana
+ taskana-routing-parent
+ 8.0.0-SNAPSHOT
+ ../pom.xml
+
+
+
+
+ org.docx4j
+ docx4j-JAXB-ReferenceImpl
+ ${version.docx4j}
+
+
+
+ org.camunda.bpm.model
+ camunda-dmn-model
+ ${version.camunda.dmn}
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+ org.apache.logging.log4j
+ log4j-api
+ test
+
+
+ org.apache.logging.log4j
+ log4j-core
+ test
+
+
+ org.apache.logging.log4j
+ log4j-slf4j2-impl
+ test
+
+
+
+
+
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${version.maven.resources}
+
+
+ copy-license-to-classes-folder
+ process-test-classes
+
+ copy-resources
+
+
+
+ ${project.build.directory}/classes
+
+
+
+ .
+
+ LICENSE
+
+
+
+ .
+
+ LEGAL_NOTICE.md
+
+
+
+ .
+
+ README.md
+
+
+
+
+
+
+
+
+
+
+
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/AdvancedSpreadsheetAdapter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/AdvancedSpreadsheetAdapter.java
new file mode 100644
index 0000000000..b91f910b2b
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/AdvancedSpreadsheetAdapter.java
@@ -0,0 +1,101 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetRow;
+import org.camunda.bpm.dmn.xlsx.elements.HeaderValuesContainer;
+import org.camunda.bpm.model.dmn.HitPolicy;
+
+public class AdvancedSpreadsheetAdapter extends BaseAdapter {
+
+ public InputOutputColumns determineInputOutputs(Spreadsheet context) {
+ Set inputColumns = new LinkedHashSet<>();
+ Set outputColumns = new LinkedHashSet<>();
+
+ SpreadsheetRow headerRow = context.getRows().get(0);
+
+ List cells = headerRow.getCells();
+
+ for (SpreadsheetCell indexedCell : cells) {
+ if("input".equalsIgnoreCase(context.resolveCellContent(indexedCell))) {
+ inputColumns.add(indexedCell.getColumn());
+ }
+ if("output".equalsIgnoreCase(context.resolveCellContent(indexedCell))) {
+ outputColumns.add(indexedCell.getColumn());
+ }
+ }
+
+ InputOutputColumns columns = new InputOutputColumns();
+ int idCounter = 0;
+ for (String column : inputColumns) {
+ idCounter++;
+ HeaderValuesContainer hvc = new HeaderValuesContainer();
+ hvc.setId("input" + idCounter);
+ fillHvc(context, column, hvc);
+ columns.addInputHeader(hvc);
+ }
+ idCounter= 0;
+ for (String column : outputColumns) {
+ idCounter++;
+ HeaderValuesContainer hvc = new HeaderValuesContainer();
+ hvc.setId("output" + idCounter);
+ fillHvc(context, column, hvc);
+ columns.addOutputHeader(hvc);
+ }
+
+ return columns;
+ }
+
+ public HitPolicy determineHitPolicy(Spreadsheet context) {
+ if (context.getRows().size() < 4) {
+ return null;
+ }
+ SpreadsheetRow row = context.getRows().get(4);
+ if (row.getCell("A") != null) {
+ final String hitPolicyString = context.resolveCellContent(row.getCell("A")).toUpperCase();
+ return HitPolicy.valueOf(hitPolicyString);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public List determineRuleRows(Spreadsheet context) {
+ List rows = context.getRows();
+ return rows.subList(5, rows.size());
+ }
+
+ public int numberHeaderRows() {
+ return 5;
+ }
+
+ private void fillHvc(Spreadsheet context, String column, HeaderValuesContainer hvc) {
+ SpreadsheetCell cell;
+ cell = context.getRows().get(1).getCell(column);
+ hvc.setLabel(context.resolveCellContent(cell));
+ cell = context.getRows().get(2).getCell(column);
+ hvc.setExpressionLanguage(context.resolveCellContent(cell));
+ cell = context.getRows().get(3).getCell(column);
+ hvc.setText(context.resolveCellContent(cell));
+ cell = context.getRows().get(4).getCell(column);
+ hvc.setTypeRef(context.resolveCellContent(cell));
+ hvc.setColumn(column);
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/BaseAdapter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/BaseAdapter.java
new file mode 100644
index 0000000000..ccb7053865
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/BaseAdapter.java
@@ -0,0 +1,32 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.CellContentHandler;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetAdapter;
+
+public abstract class BaseAdapter implements SpreadsheetAdapter {
+
+ @Override
+ public String determineDecisionName(Spreadsheet context) {
+ return context.getName();
+ }
+
+ @Override
+ public List getCellContentHandlers(
+ Spreadsheet context) {
+ return CellContentHandler.DEFAULT_HANDLERS;
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/CellContentHandler.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/CellContentHandler.java
new file mode 100644
index 0000000000..87fade6347
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/CellContentHandler.java
@@ -0,0 +1,30 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public interface CellContentHandler {
+
+ public static final List DEFAULT_HANDLERS = new ArrayList<>();
+
+ boolean canConvert(SpreadsheetCell cell, Spreadsheet spreadsheet);
+
+ String convert(SpreadsheetCell cell, Spreadsheet spreadsheet);
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnConversionContext.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnConversionContext.java
new file mode 100644
index 0000000000..af218a7ce5
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnConversionContext.java
@@ -0,0 +1,50 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.camunda.bpm.dmn.xlsx.elements.IndexedDmnColumns;
+
+/**
+ * @author Thorben Lindhauer
+ *
+ */
+public class DmnConversionContext {
+
+ protected final List cellContentHandlers;
+ protected final org.camunda.bpm.dmn.xlsx.XlsxWorksheetContext worksheetContext;
+
+ protected IndexedDmnColumns indexedDmnColumns = new IndexedDmnColumns();
+
+ public DmnConversionContext(
+ XlsxWorksheetContext worksheetContext, List cellContentHandlers) {
+ this.worksheetContext = worksheetContext;
+ this.cellContentHandlers = cellContentHandlers;
+ }
+
+ public String resolveCellValue(SpreadsheetCell cell) {
+ for (CellContentHandler contentHandler : cellContentHandlers) {
+ if (contentHandler.canConvert(cell, worksheetContext)) {
+ return contentHandler.convert(cell, worksheetContext);
+ }
+ }
+ throw new RuntimeException("cannot parse cell content, unsupported format");
+
+ }
+
+ public IndexedDmnColumns getIndexedDmnColumns() {
+ return indexedDmnColumns;
+ }
+
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueNumberConverter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueNumberConverter.java
new file mode 100644
index 0000000000..c875f41758
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueNumberConverter.java
@@ -0,0 +1,33 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.xlsx4j.sml.STCellType;
+
+/**
+ * @author Thorben Lindhauer
+ *
+ */
+public class DmnValueNumberConverter implements CellContentHandler {
+
+ public boolean canConvert(SpreadsheetCell cell, Spreadsheet context) {
+ return STCellType.N.equals(cell.getRaw().getT());
+ }
+
+ public String convert(SpreadsheetCell cell, Spreadsheet context) {
+ return cell.getRaw().getV();
+ }
+
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueRangeConverter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueRangeConverter.java
new file mode 100644
index 0000000000..5c810660e6
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueRangeConverter.java
@@ -0,0 +1,49 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.regex.Pattern;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.xlsx4j.sml.Cell;
+import org.xlsx4j.sml.STCellType;
+
+/**
+ * @author Thorben Lindhauer
+ *
+ */
+public class DmnValueRangeConverter implements CellContentHandler {
+
+ public static final Pattern RANGE_REGEX = Pattern.compile("[\\[\\]](?:[0-9.]+|(?:date and time\\(.+\\)))\\.\\.(?:[0-9.]+|(?:date and time\\(.+\\)))[\\[\\]]");
+
+ @Override
+ public boolean canConvert(SpreadsheetCell cell, Spreadsheet context) {
+ Cell rawCell = cell.getRaw();
+
+ if (STCellType.S.equals(rawCell.getT()))
+ {
+ String content = context.resolveCellContent(cell);
+ return RANGE_REGEX.matcher(content).matches();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public String convert(SpreadsheetCell cell, Spreadsheet context) {
+ return context.resolveCellContent(cell);
+ }
+
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueStringConverter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueStringConverter.java
new file mode 100644
index 0000000000..8a9d797bbb
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/DmnValueStringConverter.java
@@ -0,0 +1,34 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.xlsx4j.sml.STCellType;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class DmnValueStringConverter implements CellContentHandler {
+
+ public static final DmnValueStringConverter INSTANCE = new DmnValueStringConverter();
+
+ public boolean canConvert(SpreadsheetCell cell, Spreadsheet context) {
+ return STCellType.S.equals(cell.getRaw().getT());
+ }
+
+ public String convert(SpreadsheetCell cell, Spreadsheet context) {
+ String content = context.resolveCellContent(cell);
+ return "\"" + content + "\"";
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/FeelSimpleUnaryTestConverter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/FeelSimpleUnaryTestConverter.java
new file mode 100644
index 0000000000..174a9a0ab1
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/FeelSimpleUnaryTestConverter.java
@@ -0,0 +1,40 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import org.camunda.bpm.dmn.xlsx.CellContentHandler;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.xlsx4j.sml.Cell;
+import org.xlsx4j.sml.STCellType;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class FeelSimpleUnaryTestConverter implements CellContentHandler {
+
+ public boolean canConvert(SpreadsheetCell cell, Spreadsheet context) {
+ Cell rawCell = cell.getRaw();
+
+ if (!STCellType.S.equals(rawCell.getT())) {
+ return false;
+ }
+
+ String rawContent = context.resolveCellContent(cell);
+ return rawContent.startsWith(">") || rawContent.startsWith("<");
+ }
+
+ public String convert(SpreadsheetCell cell, Spreadsheet context) {
+ return context.resolveCellContent(cell);
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/InputOutputColumns.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/InputOutputColumns.java
new file mode 100644
index 0000000000..541d969845
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/InputOutputColumns.java
@@ -0,0 +1,47 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.elements.HeaderValuesContainer;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class InputOutputColumns {
+
+ protected List inputHeaders;
+ protected List outputHeaders;
+
+ public InputOutputColumns() {
+ this.inputHeaders = new ArrayList();
+ this.outputHeaders = new ArrayList();
+ }
+
+ public void addOutputHeader(HeaderValuesContainer hvc) {
+ this.outputHeaders.add(hvc);
+ }
+
+ public void addInputHeader(HeaderValuesContainer hvc) {
+ this.inputHeaders.add(hvc);
+ }
+
+ public List getOutputHeaders() {
+ return outputHeaders;
+ }
+
+ public List getInputHeaders() {
+ return inputHeaders;
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/SimpleInputOutputDetectionStrategy.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/SimpleInputOutputDetectionStrategy.java
new file mode 100644
index 0000000000..0cb5cd6ee1
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/SimpleInputOutputDetectionStrategy.java
@@ -0,0 +1,73 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.BaseAdapter;
+import org.camunda.bpm.dmn.xlsx.InputOutputColumns;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetRow;
+import org.camunda.bpm.dmn.xlsx.elements.HeaderValuesContainer;
+import org.camunda.bpm.model.dmn.HitPolicy;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class SimpleInputOutputDetectionStrategy extends BaseAdapter {
+
+ public org.camunda.bpm.dmn.xlsx.InputOutputColumns determineInputOutputs(Spreadsheet context) {
+
+ SpreadsheetRow headerRow = context.getRows().get(0);
+
+ if (!headerRow.hasCells()) {
+ throw new RuntimeException(
+ "A dmn table requires at least one output; the header row contains no entries");
+ }
+
+ org.camunda.bpm.dmn.xlsx.InputOutputColumns ioColumns = new InputOutputColumns();
+
+ List cells = headerRow.getCells();
+ HeaderValuesContainer hvc = new HeaderValuesContainer();
+ SpreadsheetCell outputCell = cells.get(cells.size() - 1);
+ fillHvc(outputCell, context, hvc);
+ hvc.setId("Output" + outputCell.getColumn());
+
+ ioColumns.addOutputHeader(hvc);
+
+ for (SpreadsheetCell inputCell : cells.subList(0, cells.size() - 1)) {
+ hvc = new HeaderValuesContainer();
+ fillHvc(inputCell, context, hvc);
+ hvc.setId("Input" + inputCell.getColumn());
+ ioColumns.addInputHeader(hvc);
+ }
+
+ return ioColumns;
+ }
+
+ @Override
+ public HitPolicy determineHitPolicy(Spreadsheet context) {
+ return null;
+ }
+
+ @Override
+ public List determineRuleRows(Spreadsheet context) {
+ List rows = context.getRows();
+ return rows.subList(1, rows.size());
+ }
+
+ private void fillHvc(SpreadsheetCell cell, Spreadsheet context, HeaderValuesContainer hvc) {
+ hvc.setText(context.resolveCellContent(cell));
+ hvc.setColumn(cell.getColumn());
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/StaticInputOutputDetectionStrategy.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/StaticInputOutputDetectionStrategy.java
new file mode 100644
index 0000000000..9337502ffa
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/StaticInputOutputDetectionStrategy.java
@@ -0,0 +1,77 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.List;
+import java.util.Set;
+import org.camunda.bpm.dmn.xlsx.BaseAdapter;
+import org.camunda.bpm.dmn.xlsx.InputOutputColumns;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetRow;
+import org.camunda.bpm.dmn.xlsx.elements.HeaderValuesContainer;
+import org.camunda.bpm.model.dmn.HitPolicy;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class StaticInputOutputDetectionStrategy extends BaseAdapter {
+
+ protected Set inputColumns;
+ protected Set outputColumns;
+
+ public StaticInputOutputDetectionStrategy(Set inputColumns, Set outputColumns) {
+ this.inputColumns = inputColumns;
+ this.outputColumns = outputColumns;
+ }
+
+ public InputOutputColumns determineInputOutputs(Spreadsheet context) {
+
+ SpreadsheetRow headerRow = context.getRows().get(0);
+
+ InputOutputColumns columns = new InputOutputColumns();
+
+ HeaderValuesContainer hvc;
+ for (SpreadsheetCell cell : headerRow.getCells()) {
+ if (inputColumns.contains(cell.getColumn())) {
+ hvc = new HeaderValuesContainer();
+ fillHvc(cell, context, hvc);
+ hvc.setId("Input" + cell.getColumn());
+ columns.addInputHeader(hvc);
+ } else if (outputColumns.contains(cell.getColumn())) {
+ hvc = new HeaderValuesContainer();
+ fillHvc(cell, context, hvc);
+ hvc.setId("Output" + cell.getColumn());
+ columns.addOutputHeader(hvc);
+ }
+ }
+
+ return columns;
+ }
+
+ @Override
+ public HitPolicy determineHitPolicy(Spreadsheet context) {
+ return null;
+ }
+
+ @Override
+ public List determineRuleRows(Spreadsheet context) {
+ List rows = context.getRows();
+ return rows.subList(1, rows.size());
+ }
+
+ private void fillHvc(SpreadsheetCell cell, Spreadsheet context, HeaderValuesContainer hvc) {
+ hvc.setText(context.resolveCellContent(cell));
+ hvc.setColumn(cell.getColumn());
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxConverter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxConverter.java
new file mode 100644
index 0000000000..4a5cf4fcf2
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxConverter.java
@@ -0,0 +1,85 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.io.InputStream;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetAdapter;
+import org.camunda.bpm.model.dmn.DmnModelInstance;
+import org.docx4j.openpackaging.exceptions.Docx4JException;
+import org.docx4j.openpackaging.packages.SpreadsheetMLPackage;
+import org.docx4j.openpackaging.parts.DocPropsExtendedPart;
+import org.docx4j.openpackaging.parts.SpreadsheetML.SharedStrings;
+import org.docx4j.openpackaging.parts.SpreadsheetML.WorkbookPart;
+import org.docx4j.openpackaging.parts.SpreadsheetML.WorksheetPart;
+
+/**
+ * @author Thorben Lindhauer
+ *
+ */
+public class XlsxConverter {
+
+ public static final String DEFAULT_HISTORY_TIME_TO_LIVE = "P180D";
+
+ protected String historyTimeToLive = DEFAULT_HISTORY_TIME_TO_LIVE;
+
+ protected SpreadsheetAdapter ioDetectionStrategy = new SimpleInputOutputDetectionStrategy();
+
+ public DmnModelInstance convert(InputStream inputStream) {
+ SpreadsheetMLPackage spreadSheetPackage = null;
+ try {
+ spreadSheetPackage = SpreadsheetMLPackage.load(inputStream);
+ } catch (Docx4JException e) {
+ // TODO: checked exception
+ throw new RuntimeException("cannot load document", e);
+ }
+
+ WorkbookPart workbookPart = spreadSheetPackage.getWorkbookPart();
+ // TODO: exception when no worksheet present
+ // TODO: make worksheet number configurable/import all worksheets?
+ org.camunda.bpm.dmn.xlsx.XlsxWorksheetContext worksheetContext = null;
+
+ WorksheetPart worksheetPart;
+ try {
+ String worksheetName;
+ DocPropsExtendedPart docPropsExtendedPart = spreadSheetPackage.getDocPropsExtendedPart();
+ if(docPropsExtendedPart!= null && docPropsExtendedPart.getContents().getTitlesOfParts() != null) {
+ worksheetName = (String) docPropsExtendedPart.getContents().getTitlesOfParts().getVector().getVariantOrI1OrI2().get(0).getValue();
+ } else {
+ worksheetName = "default";
+ }
+ worksheetPart = workbookPart.getWorksheet(0);
+ SharedStrings sharedStrings = workbookPart.getSharedStrings();
+ worksheetContext = new XlsxWorksheetContext(sharedStrings.getContents(), worksheetPart.getContents(), worksheetName);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not determine worksheet", e);
+ }
+
+ return new XlsxWorksheetConverter(worksheetContext, ioDetectionStrategy, historyTimeToLive).convert();
+ }
+
+ public SpreadsheetAdapter getIoDetectionStrategy() {
+ return ioDetectionStrategy;
+ }
+
+ public void setIoDetectionStrategy(SpreadsheetAdapter ioDetectionStrategy) {
+ this.ioDetectionStrategy = ioDetectionStrategy;
+ }
+
+ public String getHistoryTimeToLive() {
+ return historyTimeToLive;
+ }
+
+ public void setHistoryTimeToLive(String historyTimeToLive) {
+ this.historyTimeToLive = historyTimeToLive;
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxWorksheetContext.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxWorksheetContext.java
new file mode 100644
index 0000000000..1cdd78e45c
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxWorksheetContext.java
@@ -0,0 +1,81 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.CellContentHandler;
+import org.camunda.bpm.dmn.xlsx.api.Spreadsheet;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetRow;
+import org.camunda.bpm.dmn.xlsx.elements.IndexedRow;
+import org.xlsx4j.sml.CTRst;
+import org.xlsx4j.sml.CTSst;
+import org.xlsx4j.sml.Cell;
+import org.xlsx4j.sml.Row;
+import org.xlsx4j.sml.STCellType;
+import org.xlsx4j.sml.Worksheet;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class XlsxWorksheetContext implements Spreadsheet {
+
+ protected List cellContentHandlers;
+ protected CTSst sharedStrings;
+ protected Worksheet worksheet;
+ protected String worksheetName;
+
+ // cached state
+ protected List indexedRows;
+
+ public XlsxWorksheetContext(CTSst sharedStrings, Worksheet worksheet, String worksheetName) {
+ this.sharedStrings = sharedStrings;
+ this.worksheet = worksheet;
+ this.cellContentHandlers = new ArrayList<>();
+ this.worksheetName = worksheetName;
+ }
+
+ public List getRows() {
+ if (indexedRows == null) {
+ indexedRows = new ArrayList<>();
+ for (Row row : worksheet.getSheetData().getRow()) {
+ indexedRows.add(new IndexedRow(row));
+ }
+ }
+ return indexedRows;
+ }
+
+ public String resolveSharedString(int index) {
+ List siElements = sharedStrings.getSi();
+ return siElements.get(index).getT().getValue();
+ }
+
+ @Override
+ public String resolveCellContent(SpreadsheetCell cell) {
+ Cell rawCell = cell.getRaw();
+
+ STCellType cellType = rawCell.getT();
+ if (STCellType.S.equals(cellType)) {
+ int sharedStringIndex = Integer.parseInt(rawCell.getV());
+ return resolveSharedString(sharedStringIndex);
+ } else {
+ return rawCell.getV();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return worksheetName;
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxWorksheetConverter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxWorksheetConverter.java
new file mode 100644
index 0000000000..13e5f85c21
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/XlsxWorksheetConverter.java
@@ -0,0 +1,228 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx;
+
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetAdapter;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetRow;
+import org.camunda.bpm.dmn.xlsx.elements.HeaderValuesContainer;
+import org.camunda.bpm.dmn.xlsx.elements.IndexedDmnColumns;
+import org.camunda.bpm.model.dmn.Dmn;
+import org.camunda.bpm.model.dmn.DmnModelInstance;
+import org.camunda.bpm.model.dmn.HitPolicy;
+import org.camunda.bpm.model.dmn.impl.DmnModelConstants;
+import org.camunda.bpm.model.dmn.instance.Decision;
+import org.camunda.bpm.model.dmn.instance.DecisionTable;
+import org.camunda.bpm.model.dmn.instance.Definitions;
+import org.camunda.bpm.model.dmn.instance.Description;
+import org.camunda.bpm.model.dmn.instance.DmnElement;
+import org.camunda.bpm.model.dmn.instance.Input;
+import org.camunda.bpm.model.dmn.instance.InputEntry;
+import org.camunda.bpm.model.dmn.instance.InputExpression;
+import org.camunda.bpm.model.dmn.instance.NamedElement;
+import org.camunda.bpm.model.dmn.instance.Output;
+import org.camunda.bpm.model.dmn.instance.OutputEntry;
+import org.camunda.bpm.model.dmn.instance.Rule;
+import org.camunda.bpm.model.dmn.instance.Text;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class XlsxWorksheetConverter {
+
+ static
+ {
+ org.camunda.bpm.dmn.xlsx.CellContentHandler.DEFAULT_HANDLERS.add(new DmnValueRangeConverter());
+ org.camunda.bpm.dmn.xlsx.CellContentHandler.DEFAULT_HANDLERS.add(new FeelSimpleUnaryTestConverter());
+ org.camunda.bpm.dmn.xlsx.CellContentHandler.DEFAULT_HANDLERS.add(new DmnValueStringConverter());
+ CellContentHandler.DEFAULT_HANDLERS.add(new DmnValueNumberConverter());
+ }
+
+ protected final String historyTimeToLive;
+
+ protected org.camunda.bpm.dmn.xlsx.XlsxWorksheetContext worksheetContext;
+ protected org.camunda.bpm.dmn.xlsx.DmnConversionContext dmnConversionContext;
+ protected SpreadsheetAdapter spreadsheetAdapter;
+
+ public XlsxWorksheetConverter(XlsxWorksheetContext worksheetContext, SpreadsheetAdapter spreadsheetAdapter, String historyTimeToLive) {
+ this.worksheetContext = worksheetContext;
+ this.dmnConversionContext = new DmnConversionContext(worksheetContext, spreadsheetAdapter.getCellContentHandlers(worksheetContext));
+ this.spreadsheetAdapter = spreadsheetAdapter;
+ this.historyTimeToLive = historyTimeToLive;
+ }
+
+ public DmnModelInstance convert() {
+
+ DmnModelInstance dmnModel = initializeEmptyDmnModel();
+
+ Decision decision = generateElement(dmnModel, Decision.class, worksheetContext.getName());
+ decision.setName(spreadsheetAdapter.determineDecisionName(worksheetContext));
+ decision.setCamundaHistoryTimeToLiveString(historyTimeToLive);
+ dmnModel.getDefinitions().addChildElement(decision);
+
+ DecisionTable decisionTable = generateElement(dmnModel, DecisionTable.class, "decisionTable");
+ decision.addChildElement(decisionTable);
+
+ setHitPolicy(decisionTable);
+ convertInputsOutputs(dmnModel, decisionTable);
+ convertRules(dmnModel, decisionTable, spreadsheetAdapter.determineRuleRows(worksheetContext));
+
+ return dmnModel;
+ }
+
+ public E generateNamedElement(DmnModelInstance modelInstance, Class elementClass, String name) {
+ E element = generateElement(modelInstance, elementClass, name);
+ element.setName(name);
+ return element;
+ }
+
+ public E generateElement(DmnModelInstance modelInstance, Class elementClass, String id) {
+ E element = modelInstance.newInstance(elementClass);
+ element.setId(id);
+ return element;
+ }
+
+ /**
+ * With a generated id
+ */
+ public E generateElement(DmnModelInstance modelInstance, Class elementClass) {
+ // TODO: use a proper generator for random IDs
+ String generatedId = elementClass.getSimpleName() + Integer.toString((int) (Integer.MAX_VALUE * Math.random()));
+ return generateElement(modelInstance, elementClass, generatedId);
+ }
+
+ protected void setHitPolicy(DecisionTable decisionTable) {
+ HitPolicy hitPolicy = spreadsheetAdapter.determineHitPolicy(worksheetContext);
+ if (hitPolicy != null) {
+ decisionTable.setHitPolicy(hitPolicy);
+ }
+ }
+
+ protected void convertInputsOutputs(DmnModelInstance dmnModel, DecisionTable decisionTable) {
+
+ InputOutputColumns inputOutputColumns = spreadsheetAdapter.determineInputOutputs(worksheetContext);
+
+ // inputs
+ for (HeaderValuesContainer hvc : inputOutputColumns.getInputHeaders()) {
+ Input input = generateElement(dmnModel, Input.class, hvc.getId());
+ decisionTable.addChildElement(input);
+
+ // mandatory
+ InputExpression inputExpression = generateElement(dmnModel, InputExpression.class);
+ Text text = generateText(dmnModel, hvc.getText());
+ inputExpression.setText(text);
+ input.setInputExpression(inputExpression);
+
+ // optionals
+ if (hvc.getLabel() != null) {
+ input.setLabel(hvc.getLabel());
+ }
+ if (hvc.getTypeRef() != null) {
+ inputExpression.setTypeRef(hvc.getTypeRef());
+ }
+ if (hvc.getExpressionLanguage() != null) {
+ inputExpression.setExpressionLanguage(hvc.getExpressionLanguage());
+ }
+
+ dmnConversionContext.getIndexedDmnColumns().addInput(hvc.getColumn(), input);
+ }
+
+ // outputs
+ for (HeaderValuesContainer hvc : inputOutputColumns.getOutputHeaders()) {
+ Output output = generateElement(dmnModel, Output.class, hvc.getId());
+ decisionTable.addChildElement(output);
+
+ // mandatory
+ output.setName(hvc.getText());
+
+ // optionals
+ if (hvc.getLabel() != null) {
+ output.setLabel(hvc.getLabel());
+ }
+ if (hvc.getTypeRef() != null) {
+ output.setTypeRef(hvc.getTypeRef());
+ }
+
+ dmnConversionContext.getIndexedDmnColumns().addOutput(hvc.getColumn(), output);
+ }
+
+ }
+
+ protected void convertRules(DmnModelInstance dmnModel, DecisionTable decisionTable, List rulesRows) {
+ for (SpreadsheetRow rule : rulesRows) {
+ convertRule(dmnModel, decisionTable, rule);
+ }
+ }
+
+ protected void convertRule(DmnModelInstance dmnModel, DecisionTable decisionTable, SpreadsheetRow ruleRow) {
+ Rule rule = generateElement(dmnModel, Rule.class, "excelRow" + ruleRow.getRaw().getR());
+ decisionTable.addChildElement(rule);
+
+ IndexedDmnColumns dmnColumns = dmnConversionContext.getIndexedDmnColumns();
+
+ for (Input input : dmnColumns.getOrderedInputs()) {
+ String xlsxColumn = dmnColumns.getSpreadsheetColumn(input);
+ SpreadsheetCell cell = ruleRow.getCell(xlsxColumn);
+ String coordinate = xlsxColumn + ruleRow.getRaw().getR();
+
+ InputEntry inputEntry = generateElement(dmnModel, InputEntry.class, coordinate);
+ String textValue = cell != null ? dmnConversionContext.resolveCellValue(cell) : getDefaultCellContent();
+ Text text = generateText(dmnModel, textValue);
+ inputEntry.setText(text);
+ rule.addChildElement(inputEntry);
+ }
+
+ for (Output output : dmnColumns.getOrderedOutputs()) {
+ String xlsxColumn = dmnColumns.getSpreadsheetColumn(output);
+ SpreadsheetCell cell = ruleRow.getCell(xlsxColumn);
+ String coordinate = xlsxColumn + ruleRow.getRaw().getR();
+
+ OutputEntry outputEntry = generateElement(dmnModel, OutputEntry.class, coordinate);
+ String textValue = cell != null ? dmnConversionContext.resolveCellValue(cell) : getDefaultCellContent();
+ Text text = generateText(dmnModel, textValue);
+ outputEntry.setText(text);
+ rule.addChildElement(outputEntry);
+ }
+
+ SpreadsheetCell annotationCell = ruleRow.getCells().get(ruleRow.getCells().size() - 1);
+ Description description = generateDescription(dmnModel, worksheetContext.resolveCellContent(annotationCell));
+ rule.setDescription(description);
+
+ }
+
+ protected String getDefaultCellContent() {
+ return "-";
+ }
+
+ protected DmnModelInstance initializeEmptyDmnModel() {
+ DmnModelInstance dmnModel = Dmn.createEmptyModel();
+ Definitions definitions = generateNamedElement(dmnModel, Definitions.class, "definitions");
+ definitions.setNamespace(DmnModelConstants.CAMUNDA_NS);
+ dmnModel.setDefinitions(definitions);
+
+ return dmnModel;
+ }
+
+ protected Text generateText(DmnModelInstance dmnModel, String content) {
+ Text text = dmnModel.newInstance(Text.class);
+ text.setTextContent(content);
+ return text;
+ }
+
+ protected Description generateDescription(DmnModelInstance dmnModel, String content) {
+ Description description = dmnModel.newInstance(Description.class);
+ description.setTextContent(content);
+ return description;
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/Spreadsheet.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/Spreadsheet.java
new file mode 100644
index 0000000000..dc37c73a88
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/Spreadsheet.java
@@ -0,0 +1,24 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx.api;
+
+import java.util.List;
+
+public interface Spreadsheet {
+
+ List getRows();
+
+ String resolveCellContent(SpreadsheetCell cell);
+
+ String getName();
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetAdapter.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetAdapter.java
new file mode 100644
index 0000000000..bf12869dee
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetAdapter.java
@@ -0,0 +1,45 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx.api;
+
+import java.util.List;
+import org.camunda.bpm.dmn.xlsx.CellContentHandler;
+import org.camunda.bpm.dmn.xlsx.InputOutputColumns;
+import org.camunda.bpm.model.dmn.HitPolicy;
+
+/**
+ * Implement this interface to tailor the conversion process to a specific format
+ * of your excel sheets.
+ */
+public interface SpreadsheetAdapter {
+
+ InputOutputColumns determineInputOutputs(Spreadsheet spreadsheet);
+
+ /**
+ * @return those rows that contain rule
+ */
+ List determineRuleRows(Spreadsheet spreadsheet);
+
+ /**
+ * @return null to use the DMN default
+ */
+ HitPolicy determineHitPolicy(Spreadsheet spreadsheet);
+
+ String determineDecisionName(Spreadsheet spreadsheet);
+
+ /**
+ * order is important; add most specific converters first
+ */
+ List getCellContentHandlers(Spreadsheet spreadsheet);
+
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetCell.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetCell.java
new file mode 100644
index 0000000000..864987e06d
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetCell.java
@@ -0,0 +1,27 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx.api;
+
+import org.xlsx4j.sml.Cell;
+
+public interface SpreadsheetCell {
+
+ String getColumn();
+
+ int getRow();
+
+ /**
+ * @return the corresponding doc4j object => no API guarantees from here on :)
+ */
+ Cell getRaw();
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetRow.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetRow.java
new file mode 100644
index 0000000000..1b1126da51
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/api/SpreadsheetRow.java
@@ -0,0 +1,33 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx.api;
+
+import java.util.Collection;
+import java.util.List;
+import org.xlsx4j.sml.Row;
+
+public interface SpreadsheetRow {
+
+ Collection getColumns();
+
+ org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell getCell(String column);
+
+ List getCells();
+
+ boolean hasCells();
+
+ /**
+ * @return the corresponding doc4j object => no API guarantees from here on :)
+ */
+ Row getRaw();
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/CellHelper.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/CellHelper.java
new file mode 100644
index 0000000000..9c8fc53f11
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/CellHelper.java
@@ -0,0 +1,18 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx.elements;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class CellHelper {}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/HeaderValuesContainer.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/HeaderValuesContainer.java
new file mode 100644
index 0000000000..415a822cbc
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/HeaderValuesContainer.java
@@ -0,0 +1,77 @@
+package org.camunda.bpm.dmn.xlsx.elements;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class HeaderValuesContainer {
+ private static Set LANGUAGES;
+
+ static {
+ LANGUAGES = new HashSet();
+ LANGUAGES.add("javascript");
+ LANGUAGES.add("groovy");
+ LANGUAGES.add("python");
+ LANGUAGES.add("jruby");
+ LANGUAGES.add("juel");
+ LANGUAGES.add("feel");
+ }
+
+ private String id;
+ private String text;
+ private String label;
+ private String typeRef;
+ private String expressionLanguage;
+ private String column;
+
+ public String getExpressionLanguage() {
+ if (expressionLanguage != null && LANGUAGES.contains(expressionLanguage)) {
+ return expressionLanguage;
+ } else {
+ return null;
+ }
+ }
+
+ public void setExpressionLanguage(String expressionLanguage) {
+ this.expressionLanguage = expressionLanguage;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public void setLabel(String label) {
+ this.label = label;
+ }
+
+ public String getTypeRef() {
+ return typeRef;
+ }
+
+ public void setTypeRef(String typeRef) {
+ this.typeRef = typeRef;
+ }
+
+ public String getColumn() {
+ return column;
+ }
+
+ public void setColumn(String column) {
+ this.column = column;
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedCell.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedCell.java
new file mode 100644
index 0000000000..369702505a
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedCell.java
@@ -0,0 +1,57 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx.elements;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell;
+import org.xlsx4j.sml.Cell;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class IndexedCell implements SpreadsheetCell {
+
+ public static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Z]+)([0-9]+)");
+
+ protected Cell cell;
+ protected String column;
+ protected int row;
+
+ public IndexedCell(Cell cell) {
+ this.cell = cell;
+
+ String cellReference = cell.getR();
+ Matcher matcher = CELL_REF_PATTERN.matcher(cellReference);
+
+ boolean matches = matcher.matches();
+ if (!matches) {
+ throw new RuntimeException("Cannot parse cell reference " + cellReference);
+ }
+
+ column = matcher.group(1);
+ row = Integer.parseInt(matcher.group(2));
+ }
+
+ public Cell getRaw() {
+ return cell;
+ }
+
+ public String getColumn() {
+ return column;
+ }
+
+ public int getRow() {
+ return row;
+ }
+}
diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedDmnColumns.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedDmnColumns.java
new file mode 100644
index 0000000000..5e5b519057
--- /dev/null
+++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedDmnColumns.java
@@ -0,0 +1,59 @@
+/* Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.camunda.bpm.dmn.xlsx.elements;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.camunda.bpm.model.dmn.instance.Input;
+import org.camunda.bpm.model.dmn.instance.Output;
+
+/**
+ * @author Thorben Lindhauer
+ */
+public class IndexedDmnColumns {
+
+ protected Map inputColumns = new HashMap<>();
+ protected Map