Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment with dmn generation #89

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions src/main/java/org/prestoncabe/generators/SimpleKogitoDMNExample.java
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file demonstrates using KIE Java libraries to create a new DMN file. You can execute the code with mvn compile exec:java -Dexec.mainClass=org.prestoncabe.generators.SimpleKogitoDMNExample

Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package org.prestoncabe.generators;

import org.kie.dmn.model.v1_2.*;
import org.kie.dmn.model.v1_2.dmndi.*;
import org.kie.dmn.backend.marshalling.v1x.DMNMarshallerFactory;
import javax.xml.namespace.QName;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

public class SimpleKogitoDMNExample {
public static void main(String[] args) {
// Load the sample file to see how it structures QNames
try {
// Generate unique IDs
String modelId = "_" + UUID.randomUUID().toString().toUpperCase();
String modelName = "discount_calculator";
String namespaceURI = "https://kiegroup.org/dmn/" + UUID.randomUUID().toString();

// Create the DMN model definition
TDefinitions definitions = new TDefinitions();
definitions.setId(modelId);
definitions.setName(modelName);
definitions.setNamespace(namespaceURI);

// Set required namespaces
definitions.setTypeLanguage("http://www.omg.org/spec/DMN/20180521/FEEL/");
definitions.getNsContext().put("dmn", "http://www.omg.org/spec/DMN/20180521/MODEL/");
definitions.getNsContext().put("feel", "http://www.omg.org/spec/DMN/20180521/FEEL/");
definitions.getNsContext().put("kie", "http://www.drools.org/kie/dmn/1.2");
definitions.getNsContext().put("dmndi", "http://www.omg.org/spec/DMN/20180521/DMNDI/");
definitions.getNsContext().put("di", "http://www.omg.org/spec/DMN/20180521/DI/");
definitions.getNsContext().put("dc", "http://www.omg.org/spec/DMN/20180521/DC/");

// Create input data node
TInputData purchaseAmountInput = new TInputData();
String purchaseInputId = "_" + UUID.randomUUID().toString().toUpperCase();
purchaseAmountInput.setId(purchaseInputId);
purchaseAmountInput.setName("Purchase Amount");

// Create input variable
TInformationItem purchaseVariable = new TInformationItem();
purchaseVariable.setId("_" + UUID.randomUUID().toString().toUpperCase());
purchaseVariable.setName("Purchase Amount");
purchaseVariable.setTypeRef(new QName("number"));
purchaseAmountInput.setVariable(purchaseVariable);

// Create decision node
TDecision discountDecision = new TDecision();
String decisionId = "_" + UUID.randomUUID().toString().toUpperCase();
discountDecision.setId(decisionId);
discountDecision.setName("Discount Amount");

// Create decision variable
TInformationItem decisionVariable = new TInformationItem();
decisionVariable.setId("_" + UUID.randomUUID().toString().toUpperCase());
decisionVariable.setName("Discount Amount");
decisionVariable.setTypeRef(new QName("number"));
discountDecision.setVariable(decisionVariable);

// Create literal expression
TLiteralExpression expression = new TLiteralExpression();
String expressionId = "_" + UUID.randomUUID().toString().toUpperCase();
expression.setId(expressionId);
expression.setText("if Purchase Amount > 100 then Purchase Amount * 0.1 else 0");
discountDecision.setExpression(expression);

// Create information requirement
TInformationRequirement requirement = new TInformationRequirement();
String requirementId = "_" + UUID.randomUUID().toString().toUpperCase();
requirement.setId(requirementId);
TDMNElementReference reference = new TDMNElementReference();
reference.setHref("#" + purchaseInputId);
requirement.setRequiredInput(reference);
discountDecision.getInformationRequirement().add(requirement);

// Add nodes to definitions
definitions.getDrgElement().add(purchaseAmountInput);
definitions.getDrgElement().add(discountDecision);

// Marshal to XML
String dmnXML = DMNMarshallerFactory.newDefaultMarshaller().marshal(definitions);

// Add DMNDI using string manipulation
String dmndiXml = createDMNDI(purchaseInputId, decisionId, requirementId);
dmnXML = dmnXML.replace("</dmn:definitions>", dmndiXml + "\n</dmn:definitions>");

// Save the file
Path resourcesPath = Paths.get("src", "main", "resources");
Files.createDirectories(resourcesPath);
File dmnFile = new File(resourcesPath.toFile(), "discount-calculator.dmn");

try (FileWriter writer = new FileWriter(dmnFile)) {
writer.write(dmnXML);
System.out.println("DMN file saved to: " + dmnFile.getAbsolutePath());
}
} catch (Exception e) {
e.printStackTrace();
}
}

private static String createDMNDI(String inputId, String decisionId, String requirementId) {
StringBuilder dmndi = new StringBuilder();

dmndi.append(" <dmndi:DMNDI>\n");
dmndi.append(" <dmndi:DMNDiagram id=\"_" + UUID.randomUUID().toString().toUpperCase() + "\" name=\"DRG\">\n");

// Input shape
dmndi.append(" <dmndi:DMNShape id=\"dmnshape-" + inputId + "\" dmnElementRef=\"" + inputId + "\" isCollapsed=\"false\">\n");
dmndi.append(" <dmndi:DMNStyle>\n");
dmndi.append(" <dmndi:FillColor red=\"255\" green=\"255\" blue=\"255\"/>\n");
dmndi.append(" <dmndi:StrokeColor red=\"0\" green=\"0\" blue=\"0\"/>\n");
dmndi.append(" <dmndi:FontColor red=\"0\" green=\"0\" blue=\"0\"/>\n");
dmndi.append(" </dmndi:DMNStyle>\n");
dmndi.append(" <dc:Bounds x=\"100\" y=\"200\" width=\"100\" height=\"50\"/>\n");
dmndi.append(" <dmndi:DMNLabel/>\n");
dmndi.append(" </dmndi:DMNShape>\n");

// Decision shape
dmndi.append(" <dmndi:DMNShape id=\"dmnshape-" + decisionId + "\" dmnElementRef=\"" + decisionId + "\" isCollapsed=\"false\">\n");
dmndi.append(" <dmndi:DMNStyle>\n");
dmndi.append(" <dmndi:FillColor red=\"255\" green=\"255\" blue=\"255\"/>\n");
dmndi.append(" <dmndi:StrokeColor red=\"0\" green=\"0\" blue=\"0\"/>\n");
dmndi.append(" <dmndi:FontColor red=\"0\" green=\"0\" blue=\"0\"/>\n");
dmndi.append(" </dmndi:DMNStyle>\n");
dmndi.append(" <dc:Bounds x=\"100\" y=\"100\" width=\"100\" height=\"50\"/>\n");
dmndi.append(" <dmndi:DMNLabel/>\n");
dmndi.append(" </dmndi:DMNShape>\n");

// Edge
dmndi.append(" <dmndi:DMNEdge id=\"dmnedge-" + requirementId + "\" dmnElementRef=\"" + requirementId + "\">\n");
dmndi.append(" <di:waypoint x=\"150\" y=\"200\"/>\n");
dmndi.append(" <di:waypoint x=\"150\" y=\"150\"/>\n");
dmndi.append(" </dmndi:DMNEdge>\n");

dmndi.append(" </dmndi:DMNDiagram>\n");
dmndi.append(" </dmndi:DMNDI>");

return dmndi.toString();
}
}
18 changes: 18 additions & 0 deletions src/main/resources/addFive.dmn
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a simple DMN file that I fed to Claude to help it get the right "flavor" of DMN (the kind of DMN that the KIE DMN Editor creates).

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/" xmlns="https://kie.apache.org/dmn/_AD4B9F15-BC2F-4E75-BD9B-ADFE6962DEAB" xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/" xmlns:kie="http://www.drools.org/kie/dmn/1.2" xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" id="_FCAE7C08-87D4-44F9-BCC1-0F7E62E225BA" name="addFive" typeLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" namespace="https://kie.apache.org/dmn/_AD4B9F15-BC2F-4E75-BD9B-ADFE6962DEAB">
<dmn:extensionElements/>
<dmn:inputData id="_7D454F11-A7AC-4EE9-AD11-1F78C712AA5D" name="inputNumber">
<dmn:extensionElements/>
<dmn:variable id="_C01A1FA4-80B6-44C8-A29C-17094DF56D5B" name="inputNumber" typeRef="number"/>
</dmn:inputData>
<dmn:decision id="_8F6EDC09-8F81-48CD-9AEE-43397830BD45" name="inputNumberPlusFive">
<dmn:extensionElements/>
<dmn:variable id="_147B757E-0320-431A-BE58-ECC7AA43E8BE" name="inputNumberPlusFive" typeRef="number"/>
<dmn:informationRequirement id="_7BBEF904-499E-4340-9F83-AAA12DADF8F3">
<dmn:requiredInput href="#_7D454F11-A7AC-4EE9-AD11-1F78C712AA5D"/>
</dmn:informationRequirement>
<dmn:literalExpression id="_F377F036-3C75-411B-967B-F74836CD5571">
<dmn:text>inputNumber + 5</dmn:text>
</dmn:literalExpression>
</dmn:decision>
</dmn:definitions>
41 changes: 41 additions & 0 deletions src/main/resources/discount-calculator.dmn
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is example output of the SimpleKogitoDMNExample code. Note this is not idempotent because new IDs are generated for each run of the code. (this is good)

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" ?>
<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:kie="http://www.drools.org/kie/dmn/1.2" xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/" xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" id="_5B0D489F-050A-4CB0-8B45-6B4216FA14C5" name="discount_calculator" expressionLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" typeLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" namespace="https://kiegroup.org/dmn/3f9882bf-91c9-4ab8-ad14-2048ff8f2fb8">
<dmn:inputData id="_A2D6E8D4-82BE-4EA5-A062-B9DA2F5D4B78" name="Purchase Amount">
<dmn:variable id="_98473C3D-E491-4598-B5C8-25D00A0D7D76" name="Purchase Amount" typeRef="number"></dmn:variable>
</dmn:inputData>
<dmn:decision id="_A93D986E-FEEB-4513-B64E-06B415E78C92" name="Discount Amount">
<dmn:variable id="_4EAAD655-0267-4BBA-8EA0-900A738249D4" name="Discount Amount" typeRef="number"></dmn:variable>
<dmn:informationRequirement id="_9652170E-F108-4FA1-AE9D-31BE6265C2DE">
<dmn:requiredInput href="#_A2D6E8D4-82BE-4EA5-A062-B9DA2F5D4B78"></dmn:requiredInput>
</dmn:informationRequirement>
<dmn:literalExpression id="_612AC39F-FD99-4A85-AC79-283493BF9470">
<dmn:text>if Purchase Amount &gt; 100 then Purchase Amount * 0.1 else 0</dmn:text>
</dmn:literalExpression>
</dmn:decision>
<dmndi:DMNDI>
<dmndi:DMNDiagram id="_D30E7BAF-B0AB-44D4-9C93-C5FD1412F8B3" name="DRG">
<dmndi:DMNShape id="dmnshape-_A2D6E8D4-82BE-4EA5-A062-B9DA2F5D4B78" dmnElementRef="_A2D6E8D4-82BE-4EA5-A062-B9DA2F5D4B78" isCollapsed="false">
<dmndi:DMNStyle>
<dmndi:FillColor red="255" green="255" blue="255"/>
<dmndi:StrokeColor red="0" green="0" blue="0"/>
<dmndi:FontColor red="0" green="0" blue="0"/>
</dmndi:DMNStyle>
<dc:Bounds x="100" y="200" width="100" height="50"/>
<dmndi:DMNLabel/>
</dmndi:DMNShape>
<dmndi:DMNShape id="dmnshape-_A93D986E-FEEB-4513-B64E-06B415E78C92" dmnElementRef="_A93D986E-FEEB-4513-B64E-06B415E78C92" isCollapsed="false">
<dmndi:DMNStyle>
<dmndi:FillColor red="255" green="255" blue="255"/>
<dmndi:StrokeColor red="0" green="0" blue="0"/>
<dmndi:FontColor red="0" green="0" blue="0"/>
</dmndi:DMNStyle>
<dc:Bounds x="100" y="100" width="100" height="50"/>
<dmndi:DMNLabel/>
</dmndi:DMNShape>
<dmndi:DMNEdge id="dmnedge-_9652170E-F108-4FA1-AE9D-31BE6265C2DE" dmnElementRef="_9652170E-F108-4FA1-AE9D-31BE6265C2DE">
<di:waypoint x="150" y="200"/>
<di:waypoint x="150" y="150"/>
</dmndi:DMNEdge>
</dmndi:DMNDiagram>
</dmndi:DMNDI>
</dmn:definitions>