
org.kie.dmn.xls2dmn.cli.XLS2DMNParser Maven / Gradle / Ivy
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates.
*
* 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.kie.dmn.xls2dmn.cli;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.drools.decisiontable.parser.DecisionTableParser;
import org.drools.decisiontable.parser.xls.ExcelParser;
import org.drools.template.parser.DataListener;
import org.drools.template.parser.DecisionTableParseException;
import org.kie.dmn.api.marshalling.DMNMarshaller;
import org.kie.dmn.backend.marshalling.v1x.DMNMarshallerFactory;
import org.kie.dmn.feel.codegen.feel11.CodegenStringUtil;
import org.kie.dmn.model.api.DMNElementReference;
import org.kie.dmn.model.api.DRGElement;
import org.kie.dmn.model.api.Decision;
import org.kie.dmn.model.api.DecisionTable;
import org.kie.dmn.model.api.Definitions;
import org.kie.dmn.model.api.HitPolicy;
import org.kie.dmn.model.api.InformationItem;
import org.kie.dmn.model.api.InformationRequirement;
import org.kie.dmn.model.api.InputClause;
import org.kie.dmn.model.api.InputData;
import org.kie.dmn.model.api.LiteralExpression;
import org.kie.dmn.model.api.OutputClause;
import org.kie.dmn.model.v1_2.KieDMNModelInstrumentedBase;
import org.kie.dmn.model.v1_2.TDMNElementReference;
import org.kie.dmn.model.v1_2.TDecision;
import org.kie.dmn.model.v1_2.TDecisionTable;
import org.kie.dmn.model.v1_2.TDefinitions;
import org.kie.dmn.model.v1_2.TInformationItem;
import org.kie.dmn.model.v1_2.TInformationRequirement;
import org.kie.dmn.model.v1_2.TInputClause;
import org.kie.dmn.model.v1_2.TInputData;
import org.kie.dmn.model.v1_2.TLiteralExpression;
import org.kie.dmn.model.v1_2.TOutputClause;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XLS2DMNParser implements DecisionTableParser {
private static final Logger LOG = LoggerFactory.getLogger(XLS2DMNParser.class);
private final File outFile;
public XLS2DMNParser(File outFile) {
this.outFile = outFile;
}
@Override
public void parseFile(InputStream inStream) {
try {
parseWorkbook("xls2dmn", WorkbookFactory.create(inStream));
} catch (IOException e) {
throw new DecisionTableParseException(
"Failed to open Excel stream, " + "please check that the content is xls97 format.", e);
}
}
@Override
public void parseFile(File file) {
try {
parseWorkbook(removeTrailingExtension(file.getName()), WorkbookFactory.create(file, (String) null, true));
} catch (IOException e) {
throw new DecisionTableParseException(
"Failed to open Excel stream, " + "please check that the content is xls97 format.", e);
}
}
public void parseWorkbook(String dmnModelName, Workbook workbook) {
Map> overview = new HashMap<>();
DataFormatter formatter = new DataFormatter();
for (int s = 0; s < workbook.getNumberOfSheets(); s++) {
Sheet sheet = workbook.getSheetAt(s);
int maxRows = sheet.getLastRowNum();
for (int i = 0; i <= maxRows; i++) {
Row row = sheet.getRow(i);
int lastCellNum = row != null ? row.getLastCellNum() : 0;
if (lastCellNum == 0) {
continue; // skip empty row.
}
List header = new ArrayList<>();
for (Cell c : row) {
String text = formatter.formatCellValue(c);
header.add(text);
}
overview.put(sheet.getSheetName(), header);
break; // header found.
}
}
overview.entrySet().forEach(e -> LOG.debug("{}", e));
Map headerInfos = generateDTHeaderInfo(overview);
LOG.info("Sheets have been indexed as:");
headerInfos.entrySet().forEach(e -> LOG.info("{}", e));
Definitions definitions = new TDefinitions();
setDefaultNSContext(definitions);
definitions.setId("dmnid_" + dmnModelName);
definitions.setName(dmnModelName);
String namespace = "xls2dmn_" + UUID.randomUUID();
definitions.setNamespace(namespace);
definitions.getNsContext().put(XMLConstants.DEFAULT_NS_PREFIX, namespace);
definitions.setExporter("kie-dmn-xls2dmn");
appendInputData(definitions, headerInfos);
appendDecisionDT(definitions, headerInfos);
final Map> sheetListeners = new HashMap<>();
for (DTHeaderInfo hi : headerInfos.values()) {
String sheetName = hi.getSheetName();
DRGElement drgElem = definitions.getDrgElement().stream().filter(e -> e.getName().equals(sheetName)).findFirst().orElseThrow(() -> new XLS2DMNException("Unable to locate DRG element for sheet: " + sheetName));
DecisionTable dt = (DecisionTable) ((Decision) drgElem).getExpression();
DTSheetListener listener = new DTSheetListener(dt, hi);
sheetListeners.put(sheetName, Arrays.asList(listener));
}
new ExcelParser(sheetListeners).parseWorkbook(workbook);
DMNMarshaller dmnMarshaller = DMNMarshallerFactory.newDefaultMarshaller();
String xml = dmnMarshaller.marshal(definitions);
try {
Files.write(outFile.toPath(), xml.getBytes());
} catch (IOException e) {
LOG.error("Unable to write to outputfile.", e);
throw new XLS2DMNException("Unable to write to outputfile", e);
}
LOG.debug("output XML can be displayed at trace level",xml);
LOG.trace("output XML:\n{}",xml);
}
private void appendDecisionDT(Definitions definitions, Map headerInfos) {
for (DTHeaderInfo hi : headerInfos.values()) {
Decision decision = new TDecision();
decision.setName(hi.getSheetName());
decision.setId("d_" + CodegenStringUtil.escapeIdentifier(hi.getSheetName()));
InformationItem variable = new TInformationItem();
variable.setName(hi.getSheetName());
variable.setId("dvar_" + CodegenStringUtil.escapeIdentifier(hi.getSheetName()));
variable.setTypeRef(new QName("Any"));
decision.setVariable(variable);
for (String ri : hi.getRequiredInput()) {
InformationRequirement ir = new TInformationRequirement();
DMNElementReference er = new TDMNElementReference();
er.setHref("#id_" + CodegenStringUtil.escapeIdentifier(ri));
ir.setRequiredInput(er);
decision.getInformationRequirement().add(ir);
}
for (String ri : hi.getRequiredDecision()) {
InformationRequirement ir = new TInformationRequirement();
DMNElementReference er = new TDMNElementReference();
er.setHref("#d_" + CodegenStringUtil.escapeIdentifier(ri));
ir.setRequiredDecision(er);
decision.getInformationRequirement().add(ir);
}
DecisionTable dt = new TDecisionTable();
dt.setOutputLabel(hi.getSheetName());
dt.setId("ddt_" + CodegenStringUtil.escapeIdentifier(hi.getSheetName()));
dt.setHitPolicy(HitPolicy.ANY);
for (String ri : hi.getRequiredInput()) {
InputClause ic = new TInputClause();
ic.setLabel(ri);
LiteralExpression le = new TLiteralExpression();
le.setText(ri);
ic.setInputExpression(le);
dt.getInput().add(ic);
}
for (String rd : hi.getRequiredDecision()) {
InputClause ic = new TInputClause();
ic.setLabel(rd);
LiteralExpression le = new TLiteralExpression();
le.setText(rd);
ic.setInputExpression(le);
dt.getInput().add(ic);
}
OutputClause oc = new TOutputClause();
dt.getOutput().add(oc);
decision.setExpression(dt);
definitions.getDrgElement().add(decision);
}
}
private void setDefaultNSContext(Definitions definitions) {
Map nsContext = definitions.getNsContext();
nsContext.put("feel", KieDMNModelInstrumentedBase.URI_FEEL);
nsContext.put("dmn", KieDMNModelInstrumentedBase.URI_DMN);
nsContext.put("dmndi", KieDMNModelInstrumentedBase.URI_DMNDI);
nsContext.put("di", KieDMNModelInstrumentedBase.URI_DI);
nsContext.put("dc", KieDMNModelInstrumentedBase.URI_DC);
}
private void appendInputData(Definitions definitions, Map headerInfos) {
Set usedRI = new LinkedHashSet<>();
for ( DTHeaderInfo hi : headerInfos.values()) {
for(String ri : hi.getRequiredInput()) {
if (!usedRI.contains(ri)) {
InputData id = new TInputData();
id.setName(ri);
id.setId("id_"+CodegenStringUtil.escapeIdentifier(ri));
InformationItem variable = new TInformationItem();
variable.setName(ri);
variable.setId("idvar_"+CodegenStringUtil.escapeIdentifier(ri));
variable.setTypeRef(new QName("Any"));
id.setVariable(variable);
definitions.getDrgElement().add(id);
}
usedRI.add(ri);
}
}
}
private Map generateDTHeaderInfo(Map> overview) {
Map result = new HashMap<>();
for (Entry> kv : overview.entrySet()) {
String sheetName = kv.getKey();
List requiredInput = new ArrayList<>();
List requiredDecision = new ArrayList<>();
int hIndex = kv.getValue().indexOf(sheetName);
if (hIndex < 0) {
throw new XLS2DMNException("There is no result output column in sheet: " + sheetName);
}
if (hIndex != kv.getValue().size()) {
for (int i = hIndex+1; i < kv.getValue().size(); i++) {
String afterIndexValue = kv.getValue().get(i);
if (!(afterIndexValue == null || afterIndexValue.isEmpty())) {
throw new XLS2DMNException("Decision name was not last, on the right I found " + afterIndexValue);
}
}
}
for (int i = 0; i < hIndex; i++) {
String hValue = kv.getValue().get(i);
if (overview.containsKey(hValue)) {
requiredDecision.add(hValue);
} else {
requiredInput.add(hValue);
}
}
DTHeaderInfo info = new DTHeaderInfo(sheetName, kv.getValue(), hIndex, requiredInput, requiredDecision);
result.put(sheetName, info);
}
return result;
}
public static String removeTrailingExtension(String filename) {
if (filename.endsWith(".xls") || filename.endsWith(".xlsx") ) {
return filename.substring(0, filename.lastIndexOf("."));
}
return filename;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy