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.9 4.0.1 4.0.3 4.0.3 @@ -352,6 +358,7 @@ true warning true + **/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 outputColumns = new HashMap<>(); + + // as they appear in the resulting DMN table + protected List orderedInputs = new ArrayList<>(); + protected List orderedOutputs = new ArrayList<>(); + + public List getOrderedInputs() { + return orderedInputs; + } + + public List getOrderedOutputs() { + return orderedOutputs; + } + + public String getSpreadsheetColumn(Input input) { + return inputColumns.get(input); + } + + public String getSpreadsheetColumn(Output output) { + return outputColumns.get(output); + } + + public void addInput(String column, Input input) { + this.orderedInputs.add(input); + this.inputColumns.put(input, column); + } + + public void addOutput(String column, Output output) { + this.orderedOutputs.add(output); + this.outputColumns.put(output, column); + } +} diff --git a/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedRow.java b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedRow.java new file mode 100644 index 0000000000..e86de697db --- /dev/null +++ b/routing/dmn-xlsx-converter/src/main/java/org/camunda/bpm/dmn/xlsx/elements/IndexedRow.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.elements; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.camunda.bpm.dmn.xlsx.api.SpreadsheetCell; +import org.camunda.bpm.dmn.xlsx.api.SpreadsheetRow; +import org.xlsx4j.sml.Cell; +import org.xlsx4j.sml.Row; + +/** + * @author Thorben Lindhauer + * + */ +public class IndexedRow implements SpreadsheetRow { + + public static final Pattern CELL_REF_PATTERN = Pattern.compile("([A-Z]+)([0-9]+)"); + + protected Row row; + protected List cells; + protected Map cellsByColumn; + + public IndexedRow(Row row) { + this.row = row; + this.cells = new ArrayList<>(); + this.cellsByColumn = new HashMap<>(); + + for (Cell cell : row.getC()) { + IndexedCell indexedCell = new IndexedCell(cell); + String column = indexedCell.getColumn(); + cells.add(indexedCell); + cellsByColumn.put(column, indexedCell); + } + } + + public Row getRaw() { + return row; + } + + public Collection getColumns() { + return cellsByColumn.keySet(); + } + + public SpreadsheetCell getCell(String column) { + return cellsByColumn.get(column); + } + + public boolean hasCells() { + return !cells.isEmpty(); + } + + public List getCells() { + return cells; + } + + protected String extractColumn(Cell cell) { + String cellReference = cell.getR(); + Matcher matcher = CELL_REF_PATTERN.matcher(cellReference); + return matcher.group(1); + } +} diff --git a/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/DmnValueRangeMatcherTest.java b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/DmnValueRangeMatcherTest.java new file mode 100644 index 0000000000..646668693c --- /dev/null +++ b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/DmnValueRangeMatcherTest.java @@ -0,0 +1,53 @@ +/* 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 static org.assertj.core.api.Assertions.assertThat; + +import java.util.regex.Matcher; +import org.camunda.bpm.dmn.xlsx.DmnValueRangeConverter; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +/** + * @author Thorben Lindhauer + * + */ +public class DmnValueRangeMatcherTest { + + @ParameterizedTest + @CsvSource({ + "[1..9], true ", + "]1..9], true ", + "[1..9[, true ", + "]1..9[, true ", + "[100..900], true ", + "[10.1..909], true ", + "[10..90.1], true ", + "[10.1..90.1], true ", + "text, false ", + "[a..b], false ", + "[100..a], false ", + "[100..900, false ", + "100..900, false ", + "[100900], false ", + "[100.900], false ", + "[date and time(\"2018-05-17T00:00:00\")..date and time(\"2018-11-17T24:00:00\")], true" + }) + public void shouldMatchInclusiveInterval(String input, boolean shouldMatch) + { + Matcher matcher = DmnValueRangeConverter.RANGE_REGEX.matcher(input); + + assertThat(matcher.matches()).isEqualTo(shouldMatch); + } +} diff --git a/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/InputOutputDetectionStrategyTest.java b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/InputOutputDetectionStrategyTest.java new file mode 100644 index 0000000000..cbc9d13064 --- /dev/null +++ b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/InputOutputDetectionStrategyTest.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; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.InputStream; +import java.util.Collections; +import org.camunda.bpm.model.dmn.DmnModelInstance; +import org.camunda.bpm.model.dmn.instance.DecisionTable; +import org.junit.jupiter.api.Test; + +/** + * @author Thorben Lindhauer + */ +public class InputOutputDetectionStrategyTest { + + @Test + public void testStaticDetectionStrategy() { + XlsxConverter converter = new XlsxConverter(); + converter.setIoDetectionStrategy( + new StaticInputOutputDetectionStrategy(Collections.singleton("B"), + Collections.singleton("D"))); + InputStream inputStream = TestHelper.getClassPathResource("test2.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + assertThat(dmnModelInstance).isNotNull(); + + DecisionTable table = TestHelper.assertAndGetSingleDecisionTable(dmnModelInstance); + assertThat(table).isNotNull(); + assertThat(table.getInputs()).hasSize(1); + assertThat(table.getOutputs()).hasSize(1); + assertThat(table.getRules()).hasSize(4); + } + +} diff --git a/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/TestHelper.java b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/TestHelper.java new file mode 100644 index 0000000000..f5c682c6c2 --- /dev/null +++ b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/TestHelper.java @@ -0,0 +1,46 @@ +/* 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 static org.assertj.core.api.Assertions.assertThat; + +import java.io.InputStream; +import java.util.Collection; +import org.camunda.bpm.model.dmn.DmnModelInstance; +import org.camunda.bpm.model.dmn.instance.Decision; +import org.camunda.bpm.model.dmn.instance.DecisionTable; + +/** + * @author Thorben Lindhauer + * + */ +public class TestHelper { + + public static InputStream getClassPathResource(String path) { + return TestHelper.class.getClassLoader().getResourceAsStream(path); + } + + public static DecisionTable assertAndGetSingleDecisionTable(DmnModelInstance dmnModel) { + assertThat(dmnModel.getDefinitions()).isNotNull(); + Collection decisions = dmnModel.getDefinitions().getChildElementsByType(Decision.class); + assertThat(decisions).hasSize(1); + + Decision decision = decisions.iterator().next(); + assertThat(decision).isNotNull(); + + Collection decisionTables = decision.getChildElementsByType(DecisionTable.class); + assertThat(decisionTables).hasSize(1); + + return decisionTables.iterator().next(); + } +} diff --git a/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/XslxToDmnConversionTest.java b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/XslxToDmnConversionTest.java new file mode 100644 index 0000000000..cbc2554b90 --- /dev/null +++ b/routing/dmn-xlsx-converter/src/test/java/org/camunda/bpm/dmn/xlsx/XslxToDmnConversionTest.java @@ -0,0 +1,170 @@ +/* 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 static org.assertj.core.api.Assertions.assertThat; + +import java.io.InputStream; +import java.util.Iterator; +import org.camunda.bpm.model.dmn.DmnModelInstance; +import org.camunda.bpm.model.dmn.HitPolicy; +import org.camunda.bpm.model.dmn.instance.DecisionTable; +import org.camunda.bpm.model.dmn.instance.Input; +import org.camunda.bpm.model.dmn.instance.InputEntry; +import org.camunda.bpm.model.dmn.instance.Rule; +import org.junit.jupiter.api.Test; + +/** + * @author Thorben Lindhauer + * + */ +public class XslxToDmnConversionTest { + + public static final String DMN_11_NAMESPACE = "https://www.omg.org/spec/DMN/20191111/MODEL/"; + + private static final String JAVASCRIPT_SNIPPET = + "if (exp1 % 2 == 0)\n" + + " {erg = 2;}\n" + + "else\n" + + " {erg = 1;}\n" + + "erg;"; + + // TODO: assert input entry text content + + @Test + public void testSimpleConversion() { + XlsxConverter converter = new XlsxConverter(); + InputStream inputStream = TestHelper.getClassPathResource("test1.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + assertThat(dmnModelInstance).isNotNull(); + + DecisionTable table = TestHelper.assertAndGetSingleDecisionTable(dmnModelInstance); + assertThat(table).isNotNull(); + assertThat(table.getInputs()).hasSize(2); + assertThat(table.getOutputs()).hasSize(1); + assertThat(table.getRules()).hasSize(4); + } + + @Test + public void testConversionOfMixedNumberAndStringColumns() { + XlsxConverter converter = new XlsxConverter(); + InputStream inputStream = TestHelper.getClassPathResource("test2.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + assertThat(dmnModelInstance).isNotNull(); + + DecisionTable table = TestHelper.assertAndGetSingleDecisionTable(dmnModelInstance); + assertThat(table).isNotNull(); + assertThat(table.getInputs()).hasSize(3); + assertThat(table.getOutputs()).hasSize(1); + assertThat(table.getRules()).hasSize(4); + } + + @Test + public void testConversionOfEmptyCells() { + XlsxConverter converter = new XlsxConverter(); + InputStream inputStream = TestHelper.getClassPathResource("test3.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + assertThat(dmnModelInstance).isNotNull(); + + DecisionTable table = TestHelper.assertAndGetSingleDecisionTable(dmnModelInstance); + assertThat(table).isNotNull(); + assertThat(table.getInputs()).hasSize(3); + assertThat(table.getOutputs()).hasSize(1); + assertThat(table.getRules()).hasSize(4); + } + + @Test + public void testDmnNamespace() { + XlsxConverter converter = new XlsxConverter(); + InputStream inputStream = TestHelper.getClassPathResource("test1.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + + assertThat(dmnModelInstance.getDefinitions().getDomElement().getNamespaceURI()).isEqualTo(DMN_11_NAMESPACE); + } + + @Test + public void testConversionOfNullTitleOfParts() { + XlsxConverter converter = new XlsxConverter(); + InputStream inputStream = TestHelper.getClassPathResource("test4.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + assertThat(dmnModelInstance).isNotNull(); + + DecisionTable table = TestHelper.assertAndGetSingleDecisionTable(dmnModelInstance); + + assertThat(table).isNotNull(); + assertThat(table.getInputs()).hasSize(2); + assertThat(table.getOutputs()).hasSize(1); + assertThat(table.getRules()).hasSize(1); + } + + @Test + public void testConversionWithRanges() { + XlsxConverter converter = new XlsxConverter(); + InputStream inputStream = TestHelper.getClassPathResource("test5.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + assertThat(dmnModelInstance).isNotNull(); + + DecisionTable table = TestHelper.assertAndGetSingleDecisionTable(dmnModelInstance); + assertThat(table).isNotNull(); + assertThat(table.getInputs()).hasSize(1); + assertThat(table.getOutputs()).hasSize(1); + assertThat(table.getRules()).hasSize(4); + + Rule firstRule = table.getRules().iterator().next(); + + InputEntry inputEntry = firstRule.getInputEntries().iterator().next(); + String firstInput = inputEntry.getTextContent(); + assertThat(firstInput).isEqualTo("[1..2]"); + } + + @Test + public void testConversionWithComplexHeaders() { + XlsxConverter converter = new XlsxConverter(); + converter.setIoDetectionStrategy(new AdvancedSpreadsheetAdapter()); + InputStream inputStream = TestHelper.getClassPathResource("test6.xlsx"); + DmnModelInstance dmnModelInstance = converter.convert(inputStream); + assertThat(dmnModelInstance).isNotNull(); + + DecisionTable table = TestHelper.assertAndGetSingleDecisionTable(dmnModelInstance); + assertThat(table).isNotNull(); + assertThat(table.getInputs()).hasSize(2); + assertThat(table.getOutputs()).hasSize(2); + assertThat(table.getRules()).hasSize(2); + assertThat(table.getHitPolicy()).isEqualTo(HitPolicy.FIRST); + + Iterator inputIterator = table.getInputs().iterator(); + Input input = inputIterator.next(); + + assertThat(input.getId()).isEqualTo("input1"); + assertThat(input.getLabel()).isEqualTo("InputLabel1"); + assertThat(input.getInputExpression().getTypeRef()).isEqualTo("string"); + assertThat(input.getTextContent()).isEqualTo("Exp1"); + + input = inputIterator.next(); + assertThat(input.getId()).isEqualTo("input2"); + assertThat(input.getLabel()).isEqualTo("InputLabel2"); + assertThat(input.getInputExpression().getTypeRef()).isEqualTo("integer"); + assertThat(input.getInputExpression().getExpressionLanguage()).isEqualTo("javascript"); + assertThat(input.getInputExpression().getTextContent()).isEqualTo(JAVASCRIPT_SNIPPET); + + Iterator ruleIterator = table.getRules().iterator(); + Rule rule = ruleIterator.next(); + assertThat(rule.getDescription().getTextContent()).isEqualTo("Comment1"); + + InputEntry inputEntry = rule.getInputEntries().iterator().next(); + assertThat(inputEntry.getTextContent()).isEqualTo("\"Foo\""); + + rule = ruleIterator.next(); + assertThat(rule.getDescription().getTextContent()).isEqualTo("Another Comment"); + } +} diff --git a/routing/dmn-xlsx-converter/src/test/resources/log4j2-test.xml b/routing/dmn-xlsx-converter/src/test/resources/log4j2-test.xml new file mode 100644 index 0000000000..58fe9a427b --- /dev/null +++ b/routing/dmn-xlsx-converter/src/test/resources/log4j2-test.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/routing/dmn-xlsx-converter/src/test/resources/test1.xlsx b/routing/dmn-xlsx-converter/src/test/resources/test1.xlsx new file mode 100644 index 0000000000..561601f989 Binary files /dev/null and b/routing/dmn-xlsx-converter/src/test/resources/test1.xlsx differ diff --git a/routing/dmn-xlsx-converter/src/test/resources/test2.xlsx b/routing/dmn-xlsx-converter/src/test/resources/test2.xlsx new file mode 100644 index 0000000000..c74529b1de Binary files /dev/null and b/routing/dmn-xlsx-converter/src/test/resources/test2.xlsx differ diff --git a/routing/dmn-xlsx-converter/src/test/resources/test3.xlsx b/routing/dmn-xlsx-converter/src/test/resources/test3.xlsx new file mode 100644 index 0000000000..91dc41d2c7 Binary files /dev/null and b/routing/dmn-xlsx-converter/src/test/resources/test3.xlsx differ diff --git a/routing/dmn-xlsx-converter/src/test/resources/test4.xlsx b/routing/dmn-xlsx-converter/src/test/resources/test4.xlsx new file mode 100644 index 0000000000..a2e93c15a6 Binary files /dev/null and b/routing/dmn-xlsx-converter/src/test/resources/test4.xlsx differ diff --git a/routing/dmn-xlsx-converter/src/test/resources/test5.xlsx b/routing/dmn-xlsx-converter/src/test/resources/test5.xlsx new file mode 100644 index 0000000000..d46ded2dea Binary files /dev/null and b/routing/dmn-xlsx-converter/src/test/resources/test5.xlsx differ diff --git a/routing/dmn-xlsx-converter/src/test/resources/test6.xlsx b/routing/dmn-xlsx-converter/src/test/resources/test6.xlsx new file mode 100644 index 0000000000..7703ac4363 Binary files /dev/null and b/routing/dmn-xlsx-converter/src/test/resources/test6.xlsx differ diff --git a/routing/pom.xml b/routing/pom.xml index 61037a52ca..8b1197100b 100644 --- a/routing/pom.xml +++ b/routing/pom.xml @@ -17,6 +17,7 @@ + dmn-xlsx-converter taskana-spi-routing-dmn-router taskana-routing-rest diff --git a/routing/taskana-routing-rest/pom.xml b/routing/taskana-routing-rest/pom.xml index 9f4e9ec3c3..5b32ac0a44 100644 --- a/routing/taskana-routing-rest/pom.xml +++ b/routing/taskana-routing-rest/pom.xml @@ -31,6 +31,7 @@ dmn-xlsx-converter ${version.dmn-xlsx-converter} + jakarta.xml.bind jakarta.xml.bind-api