This is an automated email from the ASF dual-hosted git repository.
tiagobento pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 95867ae60f6 kie-issues#888: Decision Services & multiple DRDs: Adding
a Decision Service to a DRD (#2181)
95867ae60f6 is described below
commit 95867ae60f6d72bb4a9052e7b8fda7081fd2039a
Author: Tiago Bento <[email protected]>
AuthorDate: Tue Feb 27 17:25:24 2024 -0500
kie-issues#888: Decision Services & multiple DRDs: Adding a Decision
Service to a DRD (#2181)
---
.../dmn-editor/src/autolayout/AutolayoutButton.tsx | 4 +-
packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx | 2 +-
packages/dmn-editor/src/diagram/Diagram.tsx | 250 +++++++++++++--------
packages/dmn-editor/src/diagram/DrgNodesPanel.tsx | 2 +-
packages/dmn-editor/src/diagram/nodes/Nodes.tsx | 27 +--
.../src/externalNodes/ExternalNodesPanel.tsx | 2 +-
.../mutations/addExistingDecisionServiceToDrd.ts | 248 ++++++++++++++++++++
packages/dmn-editor/src/mutations/addOrGetDrd.ts | 2 +-
packages/dmn-editor/src/mutations/deleteImport.ts | 2 +-
packages/dmn-editor/src/mutations/deleteNode.ts | 52 +++--
.../dmn-editor/src/propertiesPanel/FontOptions.tsx | 4 +-
.../src/propertiesPanel/ShapeOptions.tsx | 8 +-
packages/dmn-editor/src/store/Store.ts | 14 +-
.../computeDecisionServiceHrefsByDecisionHrefs.ts | 69 ++++++
.../src/store/computed/computeDiagramData.ts | 65 +++---
.../src/store/computed/computeIndexes.ts | 8 +-
packages/dmn-editor/src/store/computed/initial.ts | 2 +-
...lNamespaceDeclarations.ts => xmlHrefToQName.ts} | 33 ++-
.../dmn-editor/src/xml/xmlNamespaceDeclarations.ts | 6 +-
19 files changed, 602 insertions(+), 198 deletions(-)
diff --git a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
index 17b4a44e0ea..0bf61e814c2 100644
--- a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
+++ b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
@@ -407,7 +407,7 @@ export function AutolayoutButton() {
resizeNode({
definitions: s.dmn.model.definitions,
drdIndex: s.diagram.drdIndex,
- dmnShapesByHref: s.computed(s).indexes().dmnShapesByHref,
+ dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref,
snapGrid,
change: {
index: node.data.index,
@@ -455,7 +455,7 @@ export function AutolayoutButton() {
updateDecisionServiceDividerLine({
definitions: s.dmn.model.definitions,
drdIndex: s.diagram.drdIndex,
- dmnShapesByHref: s.computed(s).indexes().dmnShapesByHref,
+ dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref,
drgElementIndex: parentNode.data.index,
shapeIndex: parentNode.data.shape.index,
snapGrid,
diff --git a/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx
b/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx
index 31f048e5d38..124788860bb 100644
--- a/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx
+++ b/packages/dmn-editor/src/dataTypes/TypeRefLabel.tsx
@@ -52,7 +52,7 @@ export function TypeRefLabel({
const parsedFeelQName = parseFeelQName(typeRef);
const xmlNamespaceName = getXmlNamespaceDeclarationName({
- model: thisDmn.model.definitions,
+ rootElement: thisDmn.model.definitions,
namespace: relativeToNamespace ?? "",
});
diff --git a/packages/dmn-editor/src/diagram/Diagram.tsx
b/packages/dmn-editor/src/diagram/Diagram.tsx
index dd84d70cc69..0703dad042c 100644
--- a/packages/dmn-editor/src/diagram/Diagram.tsx
+++ b/packages/dmn-editor/src/diagram/Diagram.tsx
@@ -120,6 +120,11 @@ import {
UnknownNode,
} from "./nodes/Nodes";
import { useExternalModels } from
"../includedModels/DmnEditorDependenciesContext";
+import { xmlHrefToQName } from "../xml/xmlHrefToQName";
+import {
+ addExistingDecisionServiceToDrd,
+ getDecisionServicePropertiesRelativeToThisDmn,
+} from "../mutations/addExistingDecisionServiceToDrd";
const isFirefox = typeof (window as any).InstallTrigger !== "undefined"; //
See
https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browsers
@@ -338,15 +343,20 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
) as ExternalNode;
// --------- This is where we draw the line between the diagram and
the model.
+
dmnEditorStoreApi.setState((state) => {
- const externalDrgElement = (
- state
- .computed(state)
- .getExternalModelTypesByNamespace(externalModelsByNamespace)
-
.dmns.get(externalNode.externalDrgElementNamespace)?.model.definitions.drgElement
?? []
- ).find((s) => s["@_id"] === externalNode.externalDrgElementId);
- if (!externalDrgElement) {
- throw new Error(`Can't find DRG element with id
'${externalNode.externalDrgElementId}'.`);
+ const externalDmnsIndex = state
+ .computed(state)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns;
+
+ const externalNodeDmn =
externalDmnsIndex.get(externalNode.externalDrgElementNamespace);
+ const externalDrgElement =
(externalNodeDmn?.model.definitions.drgElement ?? []).find(
+ (e) => e["@_id"] === externalNode.externalDrgElementId
+ );
+ if (!externalNodeDmn || !externalDrgElement) {
+ throw new Error(
+ `Can't find DRG element with id
'${externalNode.externalDrgElementId}' on/or model with namespace
'${externalNode.externalDrgElementNamespace}'.`
+ );
}
const externalNodeType =
getNodeTypeFromDmnObject(externalDrgElement)!;
@@ -357,39 +367,61 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
});
const namespaceName = getXmlNamespaceDeclarationName({
- model: state.dmn.model.definitions,
+ rootElement: state.dmn.model.definitions,
namespace: externalNode.externalDrgElementNamespace,
});
- if (!namespaceName) {
- throw new Error(`Can't find namespace name for
'${externalNode.externalDrgElementNamespace}'.`);
- }
+ const externalNodeHref = buildXmlHref({
+ namespace: externalNode.externalDrgElementNamespace,
+ id: externalNode.externalDrgElementId,
+ });
- addShape({
- definitions: state.dmn.model.definitions,
- drdIndex: state.diagram.drdIndex,
- nodeType: externalNodeType,
- shape: {
- "@_dmnElementRef": buildXmlQName({
- type: "xml-qname",
- prefix: namespaceName,
- localPart: externalDrgElement["@_id"]!,
- }),
- "@_isCollapsed": true,
- "dc:Bounds": {
- "@_x": dropPoint.x,
- "@_y": dropPoint.y,
- "@_width": defaultExternalNodeDimensions["@_width"],
- "@_height": defaultExternalNodeDimensions["@_height"],
+ if (externalDrgElement.__$$element === "decisionService") {
+ addExistingDecisionServiceToDrd({
+ decisionService: externalDrgElement,
+ decisionServiceNamespace:
externalNodeDmn.model.definitions["@_namespace"],
+ drdIndex: state.diagram.drdIndex,
+ dropPoint,
+ externalDmnsIndex,
+ thisDmnsDefinitions: state.dmn.model.definitions,
+ thisDmnsIndexedDrd: state.computed(state).indexedDrd(),
+ thisDmnsNamespace: state.dmn.model.definitions["@_namespace"],
+ });
+ } else if (externalDrgElement.__$$element === "decision") {
+ // TODO: Tiago --> Add logic for Decision contained by DS.
+ const externalNodeType =
getNodeTypeFromDmnObject(externalDrgElement)!;
+ addShape({
+ definitions: state.dmn.model.definitions,
+ drdIndex: state.diagram.drdIndex,
+ nodeType: externalNodeType,
+ shape: {
+ "@_dmnElementRef": xmlHrefToQName(externalNodeHref,
state.dmn.model.definitions),
+ "dc:Bounds": {
+ "@_x": dropPoint.x,
+ "@_y": dropPoint.y,
+ "@_width": defaultExternalNodeDimensions["@_width"],
+ "@_height": defaultExternalNodeDimensions["@_height"],
+ },
},
- },
- });
- state.diagram._selectedNodes = [
- buildXmlHref({
- namespace: externalNode.externalDrgElementNamespace,
- id: externalNode.externalDrgElementId,
- }),
- ];
+ });
+ } else {
+ const externalNodeType =
getNodeTypeFromDmnObject(externalDrgElement)!;
+ addShape({
+ definitions: state.dmn.model.definitions,
+ drdIndex: state.diagram.drdIndex,
+ nodeType: externalNodeType,
+ shape: {
+ "@_dmnElementRef": xmlHrefToQName(externalNodeHref,
state.dmn.model.definitions),
+ "dc:Bounds": {
+ "@_x": dropPoint.x,
+ "@_y": dropPoint.y,
+ "@_width": defaultExternalNodeDimensions["@_width"],
+ "@_height": defaultExternalNodeDimensions["@_height"],
+ },
+ },
+ });
+ }
+ state.diagram._selectedNodes = [externalNodeHref];
});
console.debug(`DMN DIAGRAM: Adding external node`,
JSON.stringify(externalNode));
@@ -398,31 +430,65 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
DMN15__tDefinitions["drgElement"]
>;
- const nodeType = getNodeTypeFromDmnObject(drgElement);
- if (nodeType === undefined) {
- throw new Error("DMN DIAGRAM: It wasn't possible to determine the
node type");
- }
-
dmnEditorStoreApi.setState((state) => {
+ const nodeType = getNodeTypeFromDmnObject(drgElement);
+ if (nodeType === undefined) {
+ throw new Error("DMN DIAGRAM: It wasn't possible to determine
the node type");
+ }
+
const defaultNodeDimensions = DEFAULT_NODE_SIZES[nodeType]({
snapGrid: state.diagram.snapGrid,
isAlternativeInputDataShape:
state.computed(state).isAlternativeInputDataShape(),
});
- addShape({
- definitions: state.dmn.model.definitions,
- drdIndex: state.diagram.drdIndex,
- nodeType,
- shape: {
- "@_dmnElementRef": buildXmlQName({ type: "xml-qname",
localPart: drgElement["@_id"]! }),
- "@_isCollapsed": false,
- "dc:Bounds": {
- "@_x": dropPoint.x,
- "@_y": dropPoint.y,
- "@_width": defaultNodeDimensions["@_width"],
- "@_height": defaultNodeDimensions["@_height"],
+
+ if (drgElement.__$$element === "decisionService") {
+ addExistingDecisionServiceToDrd({
+ decisionService: drgElement,
+ decisionServiceNamespace:
state.dmn.model.definitions["@_namespace"],
+ drdIndex: state.diagram.drdIndex,
+ dropPoint,
+ externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
+ .dmns,
+ thisDmnsDefinitions: state.dmn.model.definitions,
+ thisDmnsIndexedDrd: state.computed(state).indexedDrd(),
+ thisDmnsNamespace: state.dmn.model.definitions["@_namespace"],
+ });
+ } else if (drgElement.__$$element === "decision") {
+ // TODO: Tiago --> Add logic for Decision contained by DS.
+ const nodeType = getNodeTypeFromDmnObject(drgElement)!;
+ addShape({
+ definitions: state.dmn.model.definitions,
+ drdIndex: state.diagram.drdIndex,
+ nodeType,
+ shape: {
+ "@_dmnElementRef": buildXmlQName({ type: "xml-qname",
localPart: drgElement["@_id"]! }),
+ "@_isCollapsed": false,
+ "dc:Bounds": {
+ "@_x": dropPoint.x,
+ "@_y": dropPoint.y,
+ "@_width": defaultNodeDimensions["@_width"],
+ "@_height": defaultNodeDimensions["@_height"],
+ },
},
- },
- });
+ });
+ } else {
+ const nodeType = getNodeTypeFromDmnObject(drgElement)!;
+ addShape({
+ definitions: state.dmn.model.definitions,
+ drdIndex: state.diagram.drdIndex,
+ nodeType,
+ shape: {
+ "@_dmnElementRef": buildXmlQName({ type: "xml-qname",
localPart: drgElement["@_id"]! }),
+ "@_isCollapsed": false,
+ "dc:Bounds": {
+ "@_x": dropPoint.x,
+ "@_y": dropPoint.y,
+ "@_width": defaultNodeDimensions["@_width"],
+ "@_height": defaultNodeDimensions["@_height"],
+ },
+ },
+ });
+ }
});
console.debug(`DMN DIAGRAM: Adding DRG node`,
JSON.stringify(drgElement));
@@ -485,7 +551,7 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
return;
}
- const sourceNodeBounds =
state.computed(state).indexes().dmnShapesByHref.get(sourceNode.id)?.["dc:Bounds"];
+ const sourceNodeBounds =
state.computed(state).indexedDrd().dmnShapesByHref.get(sourceNode.id)?.["dc:Bounds"];
if (!sourceNodeBounds) {
return;
}
@@ -608,7 +674,7 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
resizeNode({
definitions: state.dmn.model.definitions,
drdIndex: state.diagram.drdIndex,
- dmnShapesByHref:
state.computed(state).indexes().dmnShapesByHref,
+ dmnShapesByHref:
state.computed(state).indexedDrd().dmnShapesByHref,
snapGrid: state.diagram.snapGrid,
change: {
isExternal: !!node.data.dmnObjectQName.prefix,
@@ -671,22 +737,20 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
},
});
- // FIXME: This should be inside `repositionNode` I guess?
-
- // Update nested
- // External Decision Services will have encapsulated and
output decisions, but they aren't depicted in the graph.
- if (node.type === NODE_TYPES.decisionService &&
!node.data.dmnObjectQName.prefix) {
+ // Update contained Decisions of Decision Service if in
expanded form
+ if (node.type === NODE_TYPES.decisionService &&
!(node.data.shape["@_isCollapsed"] ?? false)) {
const decisionService = node.data.dmnObject as
DMN15__tDecisionService;
- const nested = [
- ...(decisionService.outputDecision ?? []),
- ...(decisionService.encapsulatedDecision ?? []),
- ];
- for (let i = 0; i < nested.length; i++) {
- const nestedNode = state
- .computed(state)
- .getDiagramData(externalModelsByNamespace)
- .nodesById.get(nested[i]["@_href"])!;
+ const { containedDecisionHrefsRelativeToThisDmn } =
getDecisionServicePropertiesRelativeToThisDmn({
+ thisDmnsNamespace:
state.dmn.model.definitions["@_namespace"],
+ decisionService,
+ decisionServiceNamespace:
+ node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
+ });
+
+ for (let i = 0; i <
containedDecisionHrefsRelativeToThisDmn.length; i++) {
+ const diagramData =
state.computed(state).getDiagramData(externalModelsByNamespace);
+ const nestedNode =
diagramData.nodesById.get(containedDecisionHrefsRelativeToThisDmn[i])!;
const snappedNestedNodeShapeWithAppliedDelta =
snapShapePosition(
state.diagram.snapGrid,
offsetShapePosition(nestedNode.data.shape, delta)
@@ -698,23 +762,14 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
change: {
type: "absolute",
nodeType: nestedNode.type as NodeType,
- selectedEdges: state
- .computed(state)
- .getDiagramData(externalModelsByNamespace)
- .edges.map((e) => e.id),
+ selectedEdges: diagramData.edges.map((e) => e.id),
shapeIndex: nestedNode.data.shape.index,
- sourceEdgeIndexes: state
- .computed(state)
- .getDiagramData(externalModelsByNamespace)
- .edges.flatMap((e) =>
- e.source === nestedNode.id && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
- ),
- targetEdgeIndexes: state
- .computed(state)
- .getDiagramData(externalModelsByNamespace)
- .edges.flatMap((e) =>
- e.target === nestedNode.id && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
- ),
+ sourceEdgeIndexes: diagramData.edges.flatMap((e) =>
+ e.source === nestedNode.id && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
+ ),
+ targetEdgeIndexes: diagramData.edges.flatMap((e) =>
+ e.target === nestedNode.id && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
+ ),
position: snappedNestedNodeShapeWithAppliedDelta,
},
});
@@ -729,11 +784,13 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
drgEdges:
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
definitions: state.dmn.model.definitions,
drdIndex: state.diagram.drdIndex,
- dmnObjectNamespace: node.data.dmnObjectNamespace,
+ dmnObjectNamespace: node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
dmnObjectQName: node.data.dmnObjectQName,
dmnObjectId: node.data.dmnObject?.["@_id"],
nodeNature: nodeNatures[node.type as NodeType],
mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS,
+ externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
+ .dmns,
});
state.dispatch(state).diagram.setNodeStatus(node.id, {
selected: false,
@@ -1561,11 +1618,13 @@ export function KeyboardShortcuts(props: {}) {
drgEdges:
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
definitions: state.dmn.model.definitions,
drdIndex: state.diagram.drdIndex,
- dmnObjectNamespace: node.data.dmnObjectNamespace,
+ dmnObjectNamespace: node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
dmnObjectQName: node.data.dmnObjectQName,
dmnObjectId: node.data.dmnObject?.["@_id"],
nodeNature: nodeNatures[node.type as NodeType],
mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS,
+ externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
+ .dmns,
});
state.dispatch(state).diagram.setNodeStatus(node.id, {
selected: false,
@@ -1770,16 +1829,20 @@ export function KeyboardShortcuts(props: {}) {
if (
(selectedNodeIds.has(edge.source) &&
canRemoveNodeFromDrdOnly({
+ externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
definitions: state.dmn.model.definitions,
drdIndex: state.diagram.drdIndex,
- dmnObjectNamespace:
nodesById.get(edge.source)!.data.dmnObjectNamespace,
+ dmnObjectNamespace:
+ nodesById.get(edge.source)!.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
dmnObjectId:
nodesById.get(edge.source)!.data.dmnObject?.["@_id"],
})) ||
(selectedNodeIds.has(edge.target) &&
canRemoveNodeFromDrdOnly({
+ externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
definitions: state.dmn.model.definitions,
drdIndex: state.diagram.drdIndex,
- dmnObjectNamespace:
nodesById.get(edge.target)!.data.dmnObjectNamespace,
+ dmnObjectNamespace:
+ nodesById.get(edge.target)!.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
dmnObjectId:
nodesById.get(edge.target)!.data.dmnObject?.["@_id"],
}))
) {
@@ -1801,8 +1864,9 @@ export function KeyboardShortcuts(props: {}) {
const { deletedDmnShapeOnCurrentDrd: deletedShape } = deleteNode({
drgEdges: [], // Deleting from DRD only.
definitions: state.dmn.model.definitions,
+ externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
drdIndex: state.diagram.drdIndex,
- dmnObjectNamespace: node.data.dmnObjectNamespace,
+ dmnObjectNamespace: node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
dmnObjectQName: node.data.dmnObjectQName,
dmnObjectId: node.data.dmnObject?.["@_id"],
nodeNature: nodeNatures[node.type as NodeType],
@@ -1818,7 +1882,7 @@ export function KeyboardShortcuts(props: {}) {
}
}
});
- }, [x, dmnEditorStoreApi, rf]);
+ }, [x, dmnEditorStoreApi, rf, externalModelsByNamespace]);
return <></>;
}
diff --git a/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx
b/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx
index 8efbed5fac1..101bc8adbda 100644
--- a/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx
+++ b/packages/dmn-editor/src/diagram/DrgNodesPanel.tsx
@@ -36,7 +36,7 @@ export const MIME_TYPE_FOR_DMN_EDITOR_DRG_NODE =
"kie-dmn-editor--drg-node";
export function DrgNodesPanel() {
const thisDmn = useDmnEditorStore((s) => s.dmn);
- const dmnShapesByHref = useDmnEditorStore((s) =>
s.computed(s).indexes().dmnShapesByHref);
+ const dmnShapesByHref = useDmnEditorStore((s) =>
s.computed(s).indexedDrd().dmnShapesByHref);
const dmnEditorStoreApi = useDmnEditorStoreApi();
diff --git a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
index f9b263b52c5..6b9a62392f5 100644
--- a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
+++ b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
@@ -134,7 +134,6 @@ export const InputDataNode = React.memo(
nodeType: type as typeof NODE_TYPES.inputData,
snapGrid,
shape,
- isExternal,
isAlternativeInputDataShape,
});
@@ -365,7 +364,6 @@ export const DecisionNode = React.memo(
nodeType: type as typeof NODE_TYPES.decision,
snapGrid,
shape,
- isExternal,
});
const setName = useCallback<OnEditableNodeLabelChange>(
(newName: string) => {
@@ -518,7 +516,7 @@ export const BkmNode = React.memo(
const { isTargeted, isValidConnectionTarget } =
useConnectionTargetStatus(id, shouldActLikeHovered);
const className = useNodeClassName(isValidConnectionTarget, id);
- const nodeDimensions = useNodeDimensions({ nodeType: type as typeof
NODE_TYPES.bkm, snapGrid, shape, isExternal });
+ const nodeDimensions = useNodeDimensions({ nodeType: type as typeof
NODE_TYPES.bkm, snapGrid, shape });
const setName = useCallback<OnEditableNodeLabelChange>(
(newName: string) => {
dmnEditorStoreApi.setState((state) => {
@@ -658,7 +656,6 @@ export const KnowledgeSourceNode = React.memo(
nodeType: type as typeof NODE_TYPES.knowledgeSource,
snapGrid,
shape,
- isExternal,
});
const setName = useCallback<OnEditableNodeLabelChange>(
(newName: string) => {
@@ -777,7 +774,6 @@ export const TextAnnotationNode = React.memo(
nodeType: type as typeof NODE_TYPES.textAnnotation,
snapGrid,
shape,
- isExternal,
});
const setText = useCallback(
(newText: string) => {
@@ -898,7 +894,6 @@ export const DecisionServiceNode = React.memo(
nodeType: type as typeof NODE_TYPES.decisionService,
snapGrid,
shape,
- isExternal,
});
const setName = useCallback<OnEditableNodeLabelChange>(
(newName: string) => {
@@ -943,8 +938,7 @@ export const DecisionServiceNode = React.memo(
const dividerLineRef = useRef<SVGPathElement>(null);
- // External Decision Service nodes are always collapsed.
- const isCollapsed = isExternal || shape["@_isCollapsed"];
+ const isCollapsed = shape["@_isCollapsed"] ?? false;
const onCreateDataType = useDataTypeCreationCallbackForNodes(index,
decisionService["@_name"]);
@@ -965,7 +959,7 @@ export const DecisionServiceNode = React.memo(
updateDecisionServiceDividerLine({
definitions: state.dmn.model.definitions,
drdIndex: state.diagram.drdIndex,
- dmnShapesByHref: state.computed(state).indexes().dmnShapesByHref,
+ dmnShapesByHref:
state.computed(state).indexedDrd().dmnShapesByHref,
drgElementIndex: index,
shapeIndex: shape.index,
localYPosition: e.y,
@@ -1102,7 +1096,6 @@ export const GroupNode = React.memo(
nodeType: type as typeof NODE_TYPES.group,
snapGrid,
shape,
- isExternal,
});
const setName = useCallback<OnEditableNodeLabelChange>(
(newName: string) => {
@@ -1233,7 +1226,6 @@ export const UnknownNode = React.memo(
nodeType: type as typeof NODE_TYPES.unknown,
snapGrid,
shape,
- isExternal,
});
return (
@@ -1334,21 +1326,20 @@ function useNodeResizing(id: string): boolean {
return RF.useStore((s) => s.nodeInternals.get(id)?.resizing ?? false);
}
-type NodeDimensionsProps = {
+type NodeDimensionsArgs = {
snapGrid: SnapGrid;
shape: DMNDI15__DMNShape;
- isExternal: boolean;
} & (
| { nodeType: Extract<NodeType, typeof NODE_TYPES.inputData>;
isAlternativeInputDataShape: boolean }
| { nodeType: Exclude<NodeType, typeof NODE_TYPES.inputData> }
);
-function useNodeDimensions(props: NodeDimensionsProps): RF.Dimensions {
- const { nodeType, snapGrid, shape, isExternal } = props;
- const isAlternativeInputDataShape = nodeType === NODE_TYPES.inputData ?
props.isAlternativeInputDataShape : false;
+function useNodeDimensions(args: NodeDimensionsArgs): RF.Dimensions {
+ const { nodeType, snapGrid, shape } = args;
+ const isAlternativeInputDataShape = nodeType === NODE_TYPES.inputData ?
args.isAlternativeInputDataShape : false;
return useMemo(() => {
- if (nodeType === NODE_TYPES.decisionService && (isExternal ||
shape["@_isCollapsed"])) {
+ if (nodeType === NODE_TYPES.decisionService && shape["@_isCollapsed"]) {
return DECISION_SERVICE_COLLAPSED_DIMENSIONS;
}
@@ -1366,7 +1357,7 @@ function useNodeDimensions(props: NodeDimensionsProps):
RF.Dimensions {
width: snapShapeDimensions(snapGrid, shape, minSizes).width,
height: snapShapeDimensions(snapGrid, shape, minSizes).height,
};
- }, [isAlternativeInputDataShape, isExternal, shape, snapGrid, nodeType]);
+ }, [isAlternativeInputDataShape, shape, snapGrid, nodeType]);
}
function useHoveredNodeAlwaysOnTop(
diff --git a/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx
b/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx
index 659d4f7b43f..f30a5c7b437 100644
--- a/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx
+++ b/packages/dmn-editor/src/externalNodes/ExternalNodesPanel.tsx
@@ -56,7 +56,7 @@ export function ExternalNodesPanel() {
const externalDmnsByNamespace = useDmnEditorStore(
(s) =>
s.computed(s).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns
);
- const dmnShapesByHref = useDmnEditorStore((s) =>
s.computed(s).indexes().dmnShapesByHref);
+ const dmnShapesByHref = useDmnEditorStore((s) =>
s.computed(s).indexedDrd().dmnShapesByHref);
const { onRequestToResolvePath } = useDmnEditor();
const onDragStart = useCallback((event: React.DragEvent, externalNode:
ExternalNode) => {
diff --git
a/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
new file mode 100644
index 00000000000..9e6ae2f91a2
--- /dev/null
+++ b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import {
+ DMN15__tDecisionService,
+ DMN15__tDefinitions,
+} from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
+import { DECISION_SERVICE_COLLAPSED_DIMENSIONS } from
"../diagram/nodes/DefaultSizes";
+import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
+import { Computed } from "../store/Store";
+import { computeDecisionServiceHrefsByDecisionHrefs } from
"../store/computed/computeDecisionServiceHrefsByDecisionHrefs";
+import { computeIndexedDrd } from "../store/computed/computeIndexes";
+import { xmlHrefToQName } from "../xml/xmlHrefToQName";
+import { buildXmlHref, parseXmlHref } from "../xml/xmlHrefs";
+import { addShape } from "./addShape";
+import { repositionNode } from "./repositionNode";
+
+/**
+ * When adding a Decision Service to a DRD, we need to bring all its
encapsulated and output Decisions with it,
+ * copying their layout from other DRDs, or formatting with autolayout.
+ */
+export function addExistingDecisionServiceToDrd({
+ decisionServiceNamespace,
+ decisionService,
+ externalDmnsIndex,
+ thisDmnsNamespace,
+ thisDmnsDefinitions,
+ thisDmnsIndexedDrd,
+ drdIndex,
+ dropPoint,
+}: {
+ decisionServiceNamespace: string;
+ decisionService: DMN15__tDecisionService;
+ externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+ thisDmnsNamespace: string;
+ thisDmnsDefinitions: DMN15__tDefinitions;
+ thisDmnsIndexedDrd: ReturnType<Computed["indexedDrd"]>;
+ drdIndex: number;
+ dropPoint: { x: number; y: number };
+}) {
+ const decisionServiceDmnDefinitions =
+ !decisionServiceNamespace || decisionServiceNamespace === thisDmnsNamespace
+ ? thisDmnsDefinitions
+ : externalDmnsIndex.get(decisionServiceNamespace)?.model.definitions;
+ if (!decisionServiceDmnDefinitions) {
+ throw new Error(`DMN MUTATION: Can't find definitions for model with
namespace ${decisionServiceNamespace}`);
+ }
+ const { decisionServiceNamespaceForHref,
containedDecisionHrefsRelativeToThisDmn } =
+ getDecisionServicePropertiesRelativeToThisDmn({
+ thisDmnsNamespace,
+ decisionServiceNamespace,
+ decisionService,
+ });
+
+ const decisionServiceHrefRelativeToThisDmn = buildXmlHref({
+ namespace: decisionServiceNamespaceForHref,
+ id: decisionService["@_id"]!,
+ });
+
+ const decisionServiceHrefsByDecisionHrefsRelativeToThisDmn =
computeDecisionServiceHrefsByDecisionHrefs({
+ thisDmnsNamespace,
+ drgElementsNamespace: decisionServiceNamespace,
+ drgElements: decisionServiceDmnDefinitions.drgElement,
+ });
+
+ const doesThisDrdHaveConflictingDecisionService =
containedDecisionHrefsRelativeToThisDmn.some((decisionHref) =>
+ (decisionServiceHrefsByDecisionHrefsRelativeToThisDmn.get(decisionHref) ??
[]).some((d) =>
+ thisDmnsIndexedDrd.dmnShapesByHref.has(d)
+ )
+ );
+
+ if (doesThisDrdHaveConflictingDecisionService) {
+ // There's already, in this DRD, a Decision Service in expanded form that
contains a Decision that is contained by the Decision Service we're adding.
+ // As the DMN specification doesn't allow two copies of the same DRG
element to be depicted in the same DRD, we can't add the Decision Service in
expanded form.
+ // To not disallow depicting the Decision Service in this DRD, though, we
add it in collpased form.
+ addShape({
+ definitions: thisDmnsDefinitions,
+ drdIndex,
+ nodeType: NODE_TYPES.decisionService,
+ shape: {
+ "@_dmnElementRef":
xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions),
+ "@_isCollapsed": true,
+ "dc:Bounds": {
+ "@_x": dropPoint.x,
+ "@_y": dropPoint.y,
+ "@_width": DECISION_SERVICE_COLLAPSED_DIMENSIONS.width,
+ "@_height": DECISION_SERVICE_COLLAPSED_DIMENSIONS.height,
+ },
+ },
+ });
+ return;
+ }
+
+ const drds =
decisionServiceDmnDefinitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] ?? [];
+
+ let indexedDrd: ReturnType<Computed["indexedDrd"]> | undefined;
+ for (let i = 0; i < drds.length; i++) {
+ if (thisDmnsNamespace === decisionServiceNamespace && i === drdIndex) {
+ continue; // Skip the current DRD!
+ }
+
+ const _indexedDrd = computeIndexedDrd(thisDmnsNamespace,
decisionServiceDmnDefinitions, i);
+ const dsShape =
_indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn);
+ const hasCompleteExpandedDepictionOfDecisionService =
+ dsShape &&
+ !(dsShape["@_isCollapsed"] ?? false) &&
+ containedDecisionHrefsRelativeToThisDmn.every((dHref) =>
_indexedDrd.dmnShapesByHref.has(dHref));
+
+ if (hasCompleteExpandedDepictionOfDecisionService) {
+ indexedDrd = _indexedDrd;
+ break; // Found a DRD with a complete expanded depiction of the Decision
Service.
+ }
+ }
+
+ if (!indexedDrd) {
+ // There's no DRD which inclues a complete expanded depiction of the
Decision Service. Let's proceed with auto-layout.
+ // TODO: Tiago
+ } else {
+ // Let's copy the expanded depiction of the Decision Service from `drd`.
+ // Adding or moving nodes that already exist in the current DRD to inside
the Decision Service.
+ // The positions need all be relative to the Decision Service node, of
course.
+ const dsShapeOnOtherDrd =
indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn);
+ if (
+ dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
+ dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined
+ ) {
+ throw new Error(
+ `DMN MUTATION: Complete DMNShape for Decision Service with href
${decisionServiceHrefRelativeToThisDmn} should've existed on the indexed DRD.`
+ );
+ }
+
+ addShape({
+ definitions: thisDmnsDefinitions,
+ drdIndex,
+ nodeType: NODE_TYPES.decisionService,
+ shape: {
+ "@_dmnElementRef":
xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions),
+ "dc:Bounds": {
+ "@_x": dropPoint.x,
+ "@_y": dropPoint.y,
+ "@_width": dsShapeOnOtherDrd["dc:Bounds"]["@_width"],
+ "@_height": dsShapeOnOtherDrd["dc:Bounds"]["@_height"],
+ },
+ },
+ });
+
+ for (const decisionHref of containedDecisionHrefsRelativeToThisDmn) {
+ const decisionShapeOnOtherDrd =
indexedDrd.dmnShapesByHref.get(decisionHref);
+ if (
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined ||
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_width"] === undefined ||
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_height"] === undefined
+ ) {
+ throw new Error(
+ `DMN MUTATION: Complete DMNShape for Decision with href
${decisionHref} should've existed on the indexed DRD.`
+ );
+ }
+
+ const x = dropPoint.x + (decisionShapeOnOtherDrd["dc:Bounds"]["@_x"] -
dsShapeOnOtherDrd["dc:Bounds"]["@_x"]);
+ const y = dropPoint.y + (decisionShapeOnOtherDrd["dc:Bounds"]["@_y"] -
dsShapeOnOtherDrd["dc:Bounds"]["@_y"]);
+
+ const existingDecisionShape =
thisDmnsIndexedDrd.dmnShapesByHref.get(decisionHref);
+ if (existingDecisionShape) {
+ repositionNode({
+ definitions: thisDmnsDefinitions,
+ drdIndex,
+ controlWaypointsByEdge: new Map(),
+ change: {
+ nodeType: NODE_TYPES.decision,
+ type: "absolute",
+ position: { x, y },
+ shapeIndex: existingDecisionShape.index,
+ selectedEdges: [],
+ sourceEdgeIndexes: [],
+ targetEdgeIndexes: [],
+ },
+ });
+ } else {
+ const decisionNs = parseXmlHref(decisionHref).namespace;
+ const decisionDmnDefinitions =
+ !decisionNs || decisionNs === thisDmnsNamespace
+ ? thisDmnsDefinitions
+ : externalDmnsIndex.get(decisionNs)?.model.definitions;
+ if (!decisionDmnDefinitions) {
+ throw new Error(`DMN MUTATION: Can't find definitions for model with
namespace ${decisionServiceNamespace}`);
+ }
+
+ addShape({
+ definitions: thisDmnsDefinitions,
+ drdIndex,
+ nodeType: NODE_TYPES.decision,
+ shape: {
+ "@_dmnElementRef": xmlHrefToQName(decisionHref,
thisDmnsDefinitions),
+ "dc:Bounds": {
+ "@_x": x,
+ "@_y": y,
+ "@_width": decisionShapeOnOtherDrd["dc:Bounds"]["@_width"],
+ "@_height": decisionShapeOnOtherDrd["dc:Bounds"]["@_height"],
+ },
+ },
+ });
+ }
+ }
+ }
+}
+
+export function getDecisionServicePropertiesRelativeToThisDmn({
+ thisDmnsNamespace,
+ decisionServiceNamespace,
+ decisionService,
+}: {
+ thisDmnsNamespace: string;
+ decisionServiceNamespace: string;
+ decisionService: DMN15__tDecisionService;
+}) {
+ const decisionServiceNamespaceForHref =
+ decisionServiceNamespace === thisDmnsNamespace ? "" :
decisionServiceNamespace;
+
+ const containedDecisionHrefsRelativeToThisDmn = [
+ ...(decisionService.outputDecision ?? []),
+ ...(decisionService.encapsulatedDecision ?? []),
+ ].map((d) => {
+ const parsedHref = parseXmlHref(d["@_href"]);
+ return buildXmlHref({
+ namespace: !parsedHref.namespace ? decisionServiceNamespaceForHref :
parsedHref.namespace,
+ id: parsedHref.id,
+ });
+ });
+
+ return { decisionServiceNamespaceForHref,
containedDecisionHrefsRelativeToThisDmn };
+}
diff --git a/packages/dmn-editor/src/mutations/addOrGetDrd.ts
b/packages/dmn-editor/src/mutations/addOrGetDrd.ts
index a1ff36b1681..2200bfaf35c 100644
--- a/packages/dmn-editor/src/mutations/addOrGetDrd.ts
+++ b/packages/dmn-editor/src/mutations/addOrGetDrd.ts
@@ -30,7 +30,7 @@ export function addOrGetDrd({ definitions, drdIndex }: {
definitions: DMN15__tDe
// diagram
definitions["dmndi:DMNDI"] ??= {};
definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"] ??= [];
- definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex] ??= {};
+ definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex] ??= { "@_id":
generateUuid() };
const defaultDiagram =
definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex];
defaultDiagram["@_id"] ??= generateUuid();
diff --git a/packages/dmn-editor/src/mutations/deleteImport.ts
b/packages/dmn-editor/src/mutations/deleteImport.ts
index 1f9c8f1ba12..b934cb61e04 100644
--- a/packages/dmn-editor/src/mutations/deleteImport.ts
+++ b/packages/dmn-editor/src/mutations/deleteImport.ts
@@ -25,7 +25,7 @@ export function deleteImport({ definitions, index }: {
definitions: DMN15__tDefi
const [deleted] = definitions.import.splice(index, 1);
const namespaceName = getXmlNamespaceDeclarationName({
- model: definitions,
+ rootElement: definitions,
namespace: deleted["@_namespace"],
});
if (namespaceName) {
diff --git a/packages/dmn-editor/src/mutations/deleteNode.ts
b/packages/dmn-editor/src/mutations/deleteNode.ts
index 894e9c0b043..74ff8349827 100644
--- a/packages/dmn-editor/src/mutations/deleteNode.ts
+++ b/packages/dmn-editor/src/mutations/deleteNode.ts
@@ -27,6 +27,9 @@ import { buildXmlHref } from "../xml/xmlHrefs";
import { Unpacked } from "../tsExt/tsExt";
import { DrgEdge } from "../diagram/graph/graph";
import { EdgeDeletionMode, deleteEdge } from "./deleteEdge";
+import { Computed } from "../store/Store";
+import { computeDecisionServiceHrefsByDecisionHrefs } from
"../store/computed/computeDecisionServiceHrefsByDecisionHrefs";
+import { xmlHrefToQName } from "../xml/xmlHrefToQName";
export enum NodeDeletionMode {
FROM_DRG_AND_ALL_DRDS,
@@ -41,13 +44,15 @@ export function deleteNode({
dmnObjectId,
dmnObjectQName,
dmnObjectNamespace,
+ externalDmnsIndex,
mode,
}: {
definitions: DMN15__tDefinitions;
drgEdges: DrgEdge[];
drdIndex: number;
nodeNature: NodeNature;
- dmnObjectNamespace: string | undefined;
+ externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+ dmnObjectNamespace: string;
dmnObjectId: string | undefined;
dmnObjectQName: XmlQName;
mode: NodeDeletionMode;
@@ -62,6 +67,7 @@ export function deleteNode({
drdIndex,
dmnObjectNamespace,
dmnObjectId,
+ externalDmnsIndex,
})
) {
console.warn("DMN MUTATION: Cannot hide a Decision that's contained by a
Decision Service from a DRD.");
@@ -176,31 +182,41 @@ export function canRemoveNodeFromDrdOnly({
drdIndex,
dmnObjectNamespace,
dmnObjectId,
+ externalDmnsIndex,
}: {
- dmnObjectNamespace: string | undefined;
+ dmnObjectNamespace: string;
dmnObjectId: string | undefined;
definitions: DMN15__tDefinitions;
drdIndex: number;
+ externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
}) {
const { diagramElements } = addOrGetDrd({ definitions, drdIndex });
- const dmnObjectHref = buildXmlHref({ namespace: dmnObjectNamespace, id:
dmnObjectId! });
+ const dmnObjectHref = buildXmlHref({
+ namespace: dmnObjectNamespace === definitions["@_namespace"] ? "" :
dmnObjectNamespace,
+ id: dmnObjectId!,
+ });
- // FIXME: Tiago --> A Decision can be contained by more than one Decision
Service.
- const containingDecisionService = definitions.drgElement?.find(
- (drgElement) =>
- drgElement.__$$element === "decisionService" &&
- [...(drgElement.encapsulatedDecision ?? []),
...(drgElement.outputDecision ?? [])].some(
- (dd) => dd["@_href"] === dmnObjectHref
- )
- );
+ const drgElements =
+ definitions["@_namespace"] === dmnObjectNamespace
+ ? definitions.drgElement ?? []
+ :
externalDmnsIndex.get(dmnObjectNamespace)?.model.definitions.drgElement ?? [];
+
+ const decisionServiceHrefsByDecisionHrefs =
computeDecisionServiceHrefsByDecisionHrefs({
+ thisDmnsNamespace: definitions["@_namespace"],
+ drgElementsNamespace: dmnObjectNamespace,
+ drgElements,
+ });
+
+ const containingDecisionServiceHrefs =
decisionServiceHrefsByDecisionHrefs.get(dmnObjectHref) ?? [];
+
+ const isContainedByDecisionService = containingDecisionServiceHrefs.length >
0;
- const isContainingDecisionServicePresentInTheDrd =
- containingDecisionService &&
+ const isContainingDecisionServicePresentInTheDrd =
containingDecisionServiceHrefs.some((dsHref) =>
diagramElements.some(
- (s) =>
- s.__$$element === "dmndi:DMNShape" &&
- s["@_dmnElementRef"] === buildXmlQName({ type: "xml-qname", localPart:
containingDecisionService["@_id"]! })
- );
- return !containingDecisionService ||
!isContainingDecisionServicePresentInTheDrd;
+ (e) => e.__$$element === "dmndi:DMNShape" && e["@_dmnElementRef"] ===
xmlHrefToQName(dsHref, definitions)
+ )
+ );
+
+ return !isContainedByDecisionService ||
!isContainingDecisionServicePresentInTheDrd;
}
diff --git a/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx
b/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx
index 7e0a9118291..ac113a6c48c 100644
--- a/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx
+++ b/packages/dmn-editor/src/propertiesPanel/FontOptions.tsx
@@ -68,7 +68,7 @@ export function FontOptions({ startExpanded, nodeIds }: {
startExpanded: boolean
const dmnEditorStoreApi = useDmnEditorStoreApi();
const shapeStyles = useDmnEditorStore((s) =>
- nodeIds.map((nodeId) =>
s.computed(s).indexes().dmnShapesByHref.get(nodeId)?.["di:Style"])
+ nodeIds.map((nodeId) =>
s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId)?.["di:Style"])
);
const fontFamily = useMemo(() => shapeStyles[0]?.["@_fontFamily"],
[shapeStyles]);
@@ -92,7 +92,7 @@ export function FontOptions({ startExpanded, nodeIds }: {
startExpanded: boolean
const { diagramElements } = addOrGetDrd({ definitions:
s.dmn.model.definitions, drdIndex: s.diagram.drdIndex });
const shapes = nodeIds.map((nodeId) => {
- const shape = s.computed(s).indexes().dmnShapesByHref.get(nodeId);
+ const shape = s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId);
if (!shape) {
throw new Error(`DMN Shape for '${nodeId}' does not exist.`);
}
diff --git a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx
b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx
index 472efc48fe0..6e60ab02adc 100644
--- a/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx
+++ b/packages/dmn-editor/src/propertiesPanel/ShapeOptions.tsx
@@ -51,7 +51,9 @@ export function ShapeOptions({
}) {
const dmnEditorStoreApi = useDmnEditorStoreApi();
- const shapes = useDmnEditorStore((s) => nodeIds.map((nodeId) =>
s.computed(s).indexes().dmnShapesByHref.get(nodeId)));
+ const shapes = useDmnEditorStore((s) =>
+ nodeIds.map((nodeId) =>
s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId))
+ );
const shapeStyles = useMemo(() => shapes.map((shape) =>
shape?.["di:Style"]), [shapes]);
// For when a single node is selected.
@@ -82,7 +84,7 @@ export function ShapeOptions({
dmnEditorStoreApi.setState((s) => {
const { diagramElements } = addOrGetDrd({ definitions:
s.dmn.model.definitions, drdIndex: s.diagram.drdIndex });
- const index = nodeIds.map((nodeId) =>
s.computed(s).indexes().dmnShapesByHref.get(nodeId))[0]?.index ?? -1;
+ const index = nodeIds.map((nodeId) =>
s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId))[0]?.index ?? -1;
if (index < 0) {
throw new Error(`DMN Shape for '${nodeIds[0]}' does not exist.`);
}
@@ -143,7 +145,7 @@ export function ShapeOptions({
const { diagramElements } = addOrGetDrd({ definitions:
s.dmn.model.definitions, drdIndex: s.diagram.drdIndex });
const shapes = nodeIds.map((nodeId) => {
- const shape = s.computed(s).indexes().dmnShapesByHref.get(nodeId);
+ const shape = s.computed(s).indexedDrd().dmnShapesByHref.get(nodeId);
if (!shape) {
throw new Error(`DMN Shape for '${nodeId}' does not exist.`);
}
diff --git a/packages/dmn-editor/src/store/Store.ts
b/packages/dmn-editor/src/store/Store.ts
index feb47b5d0d1..61e42213672 100644
--- a/packages/dmn-editor/src/store/Store.ts
+++ b/packages/dmn-editor/src/store/Store.ts
@@ -31,7 +31,7 @@ import { computeDataTypes } from
"./computed/computeDataTypes";
import { computeDiagramData } from "./computed/computeDiagramData";
import { computeExternalModelsByType } from
"./computed/computeExternalModelsByType";
import { computeImportsByNamespace } from
"./computed/computeImportsByNamespace";
-import { computeIndexes } from "./computed/computeIndexes";
+import { computeIndexedDrd } from "./computed/computeIndexes";
import { computeIsDropTargetNodeValidForSelection } from
"./computed/computeIsDropTargetNodeValidForSelection";
enableMapSet(); // Necessary because `Computed` has a lot of Maps and Sets.
@@ -123,7 +123,7 @@ export type Computed = {
importsByNamespace(): Map<string, DMN15__tImport>;
- indexes(): ReturnType<typeof computeIndexes>;
+ indexedDrd(): ReturnType<typeof computeIndexedDrd>;
getDiagramData(e: ExternalModelsIndex | undefined): ReturnType<typeof
computeDiagramData>;
@@ -329,8 +329,12 @@ export function createDmnEditorStore(model:
State["dmn"]["model"], computedCache
);
},
- indexes: () => {
- return computedCache.cached("indexes", computeIndexes,
[s.dmn.model.definitions, s.diagram.drdIndex]);
+ indexedDrd: () => {
+ return computedCache.cached("indexedDrd", computeIndexedDrd, [
+ s.dmn.model.definitions["@_namespace"],
+ s.dmn.model.definitions,
+ s.diagram.drdIndex,
+ ]);
},
importsByNamespace: () => {
@@ -377,7 +381,7 @@ export function createDmnEditorStore(model:
State["dmn"]["model"], computedCache
s.diagram,
s.dmn.model.definitions,
s.computed(s).getExternalModelTypesByNamespace(externalModelsByNamespace),
- s.computed(s).indexes(),
+ s.computed(s).indexedDrd(),
s.computed(s).isAlternativeInputDataShape(),
]),
};
diff --git
a/packages/dmn-editor/src/store/computed/computeDecisionServiceHrefsByDecisionHrefs.ts
b/packages/dmn-editor/src/store/computed/computeDecisionServiceHrefsByDecisionHrefs.ts
new file mode 100644
index 00000000000..03bb1f530e7
--- /dev/null
+++
b/packages/dmn-editor/src/store/computed/computeDecisionServiceHrefsByDecisionHrefs.ts
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+import { getDecisionServicePropertiesRelativeToThisDmn } from
"../../mutations/addExistingDecisionServiceToDrd";
+import { buildXmlHref } from "../../xml/xmlHrefs";
+import { State } from "../Store";
+
+export function computeDecisionServiceHrefsByDecisionHrefs({
+ thisDmnsNamespace,
+ drgElementsNamespace,
+ drgElements,
+}: {
+ thisDmnsNamespace: string;
+ drgElementsNamespace: string;
+ drgElements: State["dmn"]["model"]["definitions"]["drgElement"];
+}) {
+ drgElements ??= [];
+ const decisionServiecHrefsByDecisionHrefs = new Map<string, string[]>();
+
+ for (const drgElement of drgElements) {
+ const drgElementHref = buildXmlHref({
+ namespace: drgElementsNamespace === thisDmnsNamespace ? "" :
drgElementsNamespace,
+ id: drgElement["@_id"]!,
+ });
+
+ // Decision
+ if (drgElement.__$$element === "decision") {
+ decisionServiecHrefsByDecisionHrefs.set(
+ drgElementHref,
+ decisionServiecHrefsByDecisionHrefs.get(drgElementHref) ?? []
+ );
+ }
+ // DS
+ else if (drgElement.__$$element === "decisionService") {
+ const { containedDecisionHrefsRelativeToThisDmn } =
getDecisionServicePropertiesRelativeToThisDmn({
+ thisDmnsNamespace,
+ decisionServiceNamespace: drgElementsNamespace,
+ decisionService: drgElement,
+ });
+
+ for (const containedDecisionHref of
containedDecisionHrefsRelativeToThisDmn) {
+ decisionServiecHrefsByDecisionHrefs.set(containedDecisionHref, [
+ ...(decisionServiecHrefsByDecisionHrefs.get(containedDecisionHref)
?? []),
+ drgElementHref,
+ ]);
+ }
+ } else {
+ // Ignore other elements
+ }
+ }
+
+ return decisionServiecHrefsByDecisionHrefs;
+}
diff --git a/packages/dmn-editor/src/store/computed/computeDiagramData.ts
b/packages/dmn-editor/src/store/computed/computeDiagramData.ts
index e64bebb35e9..58ac1568323 100644
--- a/packages/dmn-editor/src/store/computed/computeDiagramData.ts
+++ b/packages/dmn-editor/src/store/computed/computeDiagramData.ts
@@ -35,6 +35,7 @@ import { Unpacked } from "../../tsExt/tsExt";
import { buildXmlHref, parseXmlHref } from "../../xml/xmlHrefs";
import { TypeOrReturnType } from "../ComputedStateCache";
import { Computed, State } from "../Store";
+import { getDecisionServicePropertiesRelativeToThisDmn } from
"../../mutations/addExistingDecisionServiceToDrd";
export const NODE_LAYERS = {
GROUP_NODE: 0,
@@ -61,7 +62,7 @@ export function computeDiagramData(
diagram: State["diagram"],
definitions: State["dmn"]["model"]["definitions"],
externalModelTypesByNamespace:
TypeOrReturnType<Computed["getExternalModelTypesByNamespace"]>,
- indexes: TypeOrReturnType<Computed["indexes"]>,
+ indexedDrd: TypeOrReturnType<Computed["indexedDrd"]>,
isAlternativeInputDataShape: boolean
) {
// console.time("nodes");
@@ -93,9 +94,9 @@ export function computeDiagramData(
const ackEdge: AckEdge = ({ id, type, dmnObject, source, target }) => {
const data = {
dmnObject,
- dmnEdge: id ? indexes.dmnEdgesByDmnElementRef.get(id) : undefined,
- dmnShapeSource: indexes.dmnShapesByHref.get(source),
- dmnShapeTarget: indexes.dmnShapesByHref.get(target),
+ dmnEdge: id ? indexedDrd.dmnEdgesByDmnElementRef.get(id) : undefined,
+ dmnShapeSource: indexedDrd.dmnShapesByHref.get(source),
+ dmnShapeTarget: indexedDrd.dmnShapesByHref.get(target),
};
const edge: RF.Edge<DmnDiagramEdgeData> = {
@@ -165,7 +166,7 @@ export function computeDiagramData(
const id = buildXmlHref({ namespace: dmnObjectNamespace, id:
dmnObjectQName.localPart });
- const _shape = indexes.dmnShapesByHref.get(id);
+ const _shape = indexedDrd.dmnShapesByHref.get(id);
if (!_shape) {
drgElementsWithoutVisualRepresentationOnCurrentDrd.push(id);
return undefined;
@@ -204,11 +205,17 @@ export function computeDiagramData(
};
if (dmnObject?.__$$element === "decisionService") {
- const containedDecisions = [...(dmnObject.outputDecision ?? []),
...(dmnObject.encapsulatedDecision ?? [])];
- for (let i = 0; i < containedDecisions.length; i++) {
- parentIdsById.set(containedDecisions[i]["@_href"], data);
+ const { containedDecisionHrefsRelativeToThisDmn } =
getDecisionServicePropertiesRelativeToThisDmn({
+ thisDmnsNamespace: definitions["@_namespace"],
+ decisionServiceNamespace: dmnObjectNamespace ??
definitions["@_namespace"],
+ decisionService: dmnObject,
+ });
+
+ for (let i = 0; i < containedDecisionHrefsRelativeToThisDmn.length; i++)
{
+ parentIdsById.set(containedDecisionHrefsRelativeToThisDmn[i], data);
}
- if (shape["@_isCollapsed"] || !!dmnObjectNamespace) {
+
+ if (shape["@_isCollapsed"]) {
newNode.style = {
...newNode.style,
...DECISION_SERVICE_COLLAPSED_DIMENSIONS,
@@ -239,24 +246,6 @@ export function computeDiagramData(
}),
];
- // Assign parents & z-index to NODES
- for (let i = 0; i < localNodes.length; i++) {
- const parent = parentIdsById.get(localNodes[i].id);
- if (parent) {
- localNodes[i].data.parentRfNode = nodesById.get(
- buildXmlHref({ namespace: parent.dmnObjectNamespace, id:
parent.dmnObjectQName.localPart })
- );
- localNodes[i].extent = undefined; // Allows the node to be dragged
freely outside of parent's bounds.
- localNodes[i].zIndex = NODE_LAYERS.NESTED_NODES;
- }
-
- if (localNodes[i].type === NODE_TYPES.group) {
- localNodes[i].zIndex = NODE_LAYERS.GROUP_NODE;
- } else if (localNodes[i].type === NODE_TYPES.decisionService) {
- localNodes[i].zIndex = NODE_LAYERS.DECISION_SERVICE_NODE;
- }
- }
-
const externalDrgElementsByIdByNamespace =
[...externalModelTypesByNamespace.dmns.entries()].reduce(
(acc, [namespace, externalDmn]) => {
// Taking advantage of the loop to add the edges here...
@@ -278,12 +267,12 @@ export function computeDiagramData(
new Map<string, Map<string, { index: number; element:
Unpacked<DMN15__tDefinitions["drgElement"]> }>>()
);
- const externalNodes = [...indexes.dmnShapesByHref.entries()].flatMap(([href,
shape]) => {
+ const externalNodes =
[...indexedDrd.dmnShapesByHref.entries()].flatMap(([href, shape]) => {
if (nodesById.get(href)) {
return [];
}
- if (!nodesById.get(href) &&
!indexes.hrefsOfDmnElementRefsOfShapesPointingToExternalDmnObjects.has(href)) {
+ if (!nodesById.get(href) &&
!indexedDrd.hrefsOfDmnElementRefsOfShapesPointingToExternalDmnObjects.has(href))
{
// Unknown local node.
console.warn(`DMN DIAGRAM: Found a shape that references a local DRG
element that doesn't exist.`, shape);
const newNode = ackNode(shape.dmnElementRefQName, null, -1);
@@ -346,6 +335,24 @@ export function computeDiagramData(
assignClassesToHighlightedHierarchyNodes(diagram._selectedNodes,
nodesById, edgesById, drgEdges);
}
+ // Assign parents & z-index to NODES
+ for (let i = 0; i < sortedNodes.length; i++) {
+ const parentNodeData = parentIdsById.get(sortedNodes[i].id);
+ if (parentNodeData) {
+ sortedNodes[i].data.parentRfNode = nodesById.get(
+ buildXmlHref({ namespace: parentNodeData.dmnObjectNamespace, id:
parentNodeData.dmnObjectQName.localPart })
+ );
+ sortedNodes[i].extent = undefined; // Allows the node to be dragged
freely outside of parent's bounds.
+ sortedNodes[i].zIndex = NODE_LAYERS.NESTED_NODES;
+ }
+
+ if (sortedNodes[i].type === NODE_TYPES.group) {
+ sortedNodes[i].zIndex = NODE_LAYERS.GROUP_NODE;
+ } else if (sortedNodes[i].type === NODE_TYPES.decisionService) {
+ sortedNodes[i].zIndex = NODE_LAYERS.DECISION_SERVICE_NODE;
+ }
+ }
+
return {
drgEdges,
drgAdjacencyList,
diff --git a/packages/dmn-editor/src/store/computed/computeIndexes.ts
b/packages/dmn-editor/src/store/computed/computeIndexes.ts
index 9e5cd35e677..62bb6a68faf 100644
--- a/packages/dmn-editor/src/store/computed/computeIndexes.ts
+++ b/packages/dmn-editor/src/store/computed/computeIndexes.ts
@@ -23,7 +23,8 @@ import { KIE_DMN_UNKNOWN_NAMESPACE } from "../../Dmn15Spec";
import { buildXmlHref } from "../../xml/xmlHrefs";
import { State } from "../Store";
-export function computeIndexes(
+export function computeIndexedDrd(
+ thisDmnsNamespace: string,
definitions: State["dmn"]["model"]["definitions"],
drdIndex: State["diagram"]["drdIndex"]
) {
@@ -52,7 +53,10 @@ export function computeIndexes(
href = buildXmlHref({ namespace, id: dmnElementRefQName.localPart });
hrefsOfDmnElementRefsOfShapesPointingToExternalDmnObjects.add(href);
} else {
- href = buildXmlHref({ id: dmnElementRefQName.localPart });
+ href = buildXmlHref({
+ namespace: definitions["@_namespace"] === thisDmnsNamespace ? "" :
definitions["@_namespace"],
+ id: dmnElementRefQName.localPart,
+ });
}
dmnShapesByHref.set(href, { ...e, index: i, dmnElementRefQName });
diff --git a/packages/dmn-editor/src/store/computed/initial.ts
b/packages/dmn-editor/src/store/computed/initial.ts
index 12d509df42b..89c8d98bead 100644
--- a/packages/dmn-editor/src/store/computed/initial.ts
+++ b/packages/dmn-editor/src/store/computed/initial.ts
@@ -29,7 +29,7 @@ export const INITIAL_COMPUTED_CACHE: Cache<Computed> = {
value: undefined,
dependencies: [],
},
- indexes: {
+ indexedDrd: {
value: undefined,
dependencies: [],
},
diff --git a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
b/packages/dmn-editor/src/xml/xmlHrefToQName.ts
similarity index 56%
copy from packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
copy to packages/dmn-editor/src/xml/xmlHrefToQName.ts
index d6d67a22da6..cda3d3b46fa 100644
--- a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
+++ b/packages/dmn-editor/src/xml/xmlHrefToQName.ts
@@ -17,26 +17,25 @@
* under the License.
*/
+import { buildXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
+import { parseXmlHref } from "./xmlHrefs";
+import { getXmlNamespaceDeclarationName } from "./xmlNamespaceDeclarations";
import { XmlParserTsRootElementBaseType } from "@kie-tools/xml-parser-ts";
-import { parseXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
-export function getXmlNamespaceDeclarationName({
- model,
- namespace,
-}: {
- model: XmlParserTsRootElementBaseType | undefined;
- namespace: string;
-}) {
- const xmlnsEntry = Object.entries(model ?? {}).find(
- ([k, v]) => v === namespace && (k === "@_xmlns" ||
k.startsWith("@_xmlns:"))
- );
- if (!xmlnsEntry) {
- return undefined;
- }
+export function xmlHrefToQName(hrefString: string, rootElement:
XmlParserTsRootElementBaseType | undefined) {
+ const href = parseXmlHref(hrefString);
+
+ const qNamePrefix = href.namespace
+ ? getXmlNamespaceDeclarationName({ rootElement, namespace: href.namespace
})
+ : undefined;
- if (xmlnsEntry[0] === "@_xmlns") {
- return undefined;
+ if (href.namespace && !qNamePrefix) {
+ throw new Error(`Can't find namespace declaration for namespace
'${href.namespace}'`);
}
- return parseXmlQName(xmlnsEntry[0]).localPart;
+ return buildXmlQName({
+ type: "xml-qname",
+ localPart: href.id,
+ prefix: qNamePrefix,
+ });
}
diff --git a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
b/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
index d6d67a22da6..34c7147a123 100644
--- a/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
+++ b/packages/dmn-editor/src/xml/xmlNamespaceDeclarations.ts
@@ -21,13 +21,13 @@ import { XmlParserTsRootElementBaseType } from
"@kie-tools/xml-parser-ts";
import { parseXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
export function getXmlNamespaceDeclarationName({
- model,
+ rootElement,
namespace,
}: {
- model: XmlParserTsRootElementBaseType | undefined;
+ rootElement: XmlParserTsRootElementBaseType | undefined;
namespace: string;
}) {
- const xmlnsEntry = Object.entries(model ?? {}).find(
+ const xmlnsEntry = Object.entries(rootElement ?? {}).find(
([k, v]) => v === namespace && (k === "@_xmlns" ||
k.startsWith("@_xmlns:"))
);
if (!xmlnsEntry) {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]