diff --git a/src/main/java/de/uniba/dsg/bpmnspector/refcheck/utils/JDOMUtils.java b/src/main/java/de/uniba/dsg/bpmnspector/refcheck/utils/JDOMUtils.java index 27509b9..e7cee98 100644 --- a/src/main/java/de/uniba/dsg/bpmnspector/refcheck/utils/JDOMUtils.java +++ b/src/main/java/de/uniba/dsg/bpmnspector/refcheck/utils/JDOMUtils.java @@ -4,6 +4,7 @@ import org.jdom2.Attribute; import org.jdom2.Document; import org.jdom2.Element; +import org.jdom2.Namespace; import org.jdom2.filter.Filter; import org.jdom2.filter.Filters; import org.jdom2.util.IteratorIterable; @@ -11,6 +12,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; public class JDOMUtils { @@ -83,4 +85,16 @@ private static void addElementsFromSingleDocument( } } + public static Optional getUsedPrefixForTargetNamespace(Document document) { + Element rootElem = document.getRootElement(); + String targetNamespaceURI = rootElem.getAttributeValue("targetNamespace"); + Optional namespaceForTargetNamespace = rootElem.getNamespacesInScope().stream().filter(n -> n.getURI().equals(targetNamespaceURI)).findAny(); + if (namespaceForTargetNamespace.isPresent()) { + return Optional.of(namespaceForTargetNamespace.get().getPrefix()); + } + return Optional.empty(); + } + + + } diff --git a/src/main/java/de/uniba/dsg/bpmnspector/schematron/SchematronBPMNValidator.java b/src/main/java/de/uniba/dsg/bpmnspector/schematron/SchematronBPMNValidator.java index 761def4..77f2c5d 100644 --- a/src/main/java/de/uniba/dsg/bpmnspector/schematron/SchematronBPMNValidator.java +++ b/src/main/java/de/uniba/dsg/bpmnspector/schematron/SchematronBPMNValidator.java @@ -102,13 +102,7 @@ public ValidationResult validate(BPMNProcess process, ValidationResult validatio ext002Checker.checkConstraint002(process, validationResult); } - org.jdom2.Document documentToCheck; - if (process.getChildren() == null || process.getChildren() - .isEmpty()) { - documentToCheck = process.getProcessAsDoc(); - } else { - documentToCheck = preProcessor.preProcess(process); - } + org.jdom2.Document documentToCheck = preProcessor.preProcess(process); DOMOutputter domOutputter = new DOMOutputter(); Document w3cDoc = domOutputter .output(documentToCheck); diff --git a/src/main/java/de/uniba/dsg/bpmnspector/schematron/preprocessing/PreProcessor.java b/src/main/java/de/uniba/dsg/bpmnspector/schematron/preprocessing/PreProcessor.java index 417f8df..fc6000d 100644 --- a/src/main/java/de/uniba/dsg/bpmnspector/schematron/preprocessing/PreProcessor.java +++ b/src/main/java/de/uniba/dsg/bpmnspector/schematron/preprocessing/PreProcessor.java @@ -2,6 +2,7 @@ import de.uniba.dsg.bpmnspector.common.importer.BPMNProcess; import de.uniba.dsg.bpmnspector.common.util.ConstantHelper; +import de.uniba.dsg.bpmnspector.refcheck.utils.JDOMUtils; import org.jdom2.Attribute; import org.jdom2.Document; import org.jdom2.Element; @@ -27,6 +28,33 @@ public class PreProcessor { private static final Logger LOGGER; + private static final String ALL_QNAME_ATTRIBUTES = "//bpmn:*/@sourceRef | //bpmn:*/@targetRef | " + + "//bpmn:*/@calledElement | //bpmn:*/@processRef | " + + "//bpmn:*/@dataStoreRef | //bpmn:*/@categoryValueRef | //bpmn:*/@messageRef | " + + "//bpmn:*/@correlationKeyRef | //bpmn:*/@correlationPropertyRef | //bpmn:*/@structureRef |" + + "//bpmn:*/@evaluatesToTypeRef | //bpmn:*/@itemRef | //bpmn:*/@type | " + + "//bpmn:*/@definitionalCollaborationRef | //bpmn:*/@parameterRef | //bpmn:*/@operationRef |" + + "//bpmn:*/@calledElement | //bpmn:*/@itemSubjectRef | //bpmn:*/@dataStoreRef | " + + "//bpmn:*/@attachedToRef | //bpmn:*/@activityRef | //bpmn:*/@errorRef | //bpmn:*/@escalationRef | " + + "//bpmn:*/@source | //bpmn:*/@target | //bpmn:*/@signalRef | //bpmn:*/@partitionElementRef"; + + private static final String ALL_IDREF_ATTRIBUTES = "//bpmn:*/@sourceRef | //bpmn:*/@targetRef | //bpmn:*/@default |" + + "//bpmn:*/@inputDataRef | //bpmn:*/@outputDataRef | //bpmn:*/@dataObjectRef"; + + private static final String ALL_QNAME_ELEMENTS = "//bpmn:eventDefinitionRef | " + + "//bpmn:incoming | //bpmn:outgoing | //bpmn:dataInputRefs | " + + "//bpmn:dataOutputRefs | //bpmn:correlationPropertyRef | //bpmn:categoryValueRef |" + + "//bpmn:inMessageRef | //bpmn:outMessageRef | //bpmn:errorRef | //bpmn:choreographyRef |" + + "//bpmn:interfaceRef | //bpmn:endPointRef | //bpmn:participantRef | //bpmn:innerParticipantRef |" + + "//bpmn:outerParticipantRef | //bpmn:supports | //bpmn:resourceRef | //bpmn:supportedInterfaceRefs |" + + "//bpmn:loopDataInputRef | //bpmn:loopDataOutputRef | //bpmn:oneBehaviorEventRef |" + + "//bpmn:noneBehaviorEventRef"; + + private static final String ALL_IDREF_ELEMENTS = "//bpmn:dataInputRefs | " + + "//bpmn:optionalInputRefs | //bpmn:whileExecutingInputRefs | //bpmn:outputSetRefs | " + + "//bpmn:dataOutputRefs | //bpmn:optionalOutputRefs | //bpmn:whileExecutingOutputRefs | " + + "//bpmn:inputSetRefs | //bpmn:sourceRef | //bpmn:targetRef | //bpmn:flowNodeRef"; + private final XPathFactory xPathFactory = XPathFactory.instance(); static { @@ -47,11 +75,16 @@ public Document preProcess(BPMNProcess process) { LOGGER.info("Starting to preprocess file: " + process.getBaseURI()); Document cloneOfDoc = process.getProcessAsDoc().clone(); + + // Preprocessing can be skipped if no files are imported and there is no Prefix used for the targetNamespace + if(process.getChildren().isEmpty() && !JDOMUtils.getUsedPrefixForTargetNamespace(cloneOfDoc).isPresent()) { + LOGGER.info("Skipping preprocessing."); + return cloneOfDoc; + } + // get all nodes which potentially refer to other files - List refAttributes = setupXPathNamespaceIdsForAttributes().evaluate( - cloneOfDoc); - List refElements = setupXPathNamespaceIdsForElements().evaluate( - cloneOfDoc); + List refAttributes = createXPathExpressionForAllQNameAttributes().evaluate(cloneOfDoc); + List refElements = createXPathExpressionForAllQNameElements().evaluate(cloneOfDoc); // for each attribute: rename IDs which can be used globally refAttributes.stream() @@ -94,11 +127,17 @@ public Document preProcess(BPMNProcess process) { private void renameGlobalIds(BPMNProcess process, Attribute attribute) { String prefix = attribute.getValue().substring(0, attribute.getValue().indexOf(":")); - String newPrefix = getNewPrefixByOldPrefix(process, prefix); - LOGGER.debug("new prefix '{}' for ID {}", newPrefix, - attribute.getValue()); - attribute.setValue(attribute.getValue().replace(prefix + ":", - newPrefix + "_")); + String newPrefix; + // special treatment if a prefix is used for the local targetNamespace + if(prefix.equals(JDOMUtils.getUsedPrefixForTargetNamespace(process.getProcessAsDoc()).orElse(""))) { + // replaces usage of 'tns:ID' with 'ID' + newPrefix = ""; + } else { + newPrefix = getNewPrefixByOldPrefix(process, prefix)+"_"; + } + String newId = attribute.getValue().replace(prefix + ":", newPrefix); + LOGGER.debug("new prefix '{}' for ID '{}' - new ID: {}", newPrefix, attribute.getValue(), newId); + attribute.setValue(newId); } /** @@ -115,11 +154,18 @@ private void renameGlobalIds(BPMNProcess process, Attribute attribute) { private void renameGlobalIds(BPMNProcess process, Element element) { String prefix = element.getText().substring(0, element.getText().indexOf(":")); - String newPrefix = getNewPrefixByOldPrefix(process, prefix); - LOGGER.debug("new prefix '{}' for ID {}", newPrefix, - element.getText()); - element.setText(element.getText().replace(prefix + ":", - newPrefix + "_")); + String newPrefix; + // special treatment if a prefix is used for the local targetNamespace + if(prefix.equals(JDOMUtils.getUsedPrefixForTargetNamespace(process.getProcessAsDoc()).orElse(""))) { + // replaces usage of 'tns:ID' with 'ID' + newPrefix = ""; + } else { + newPrefix = getNewPrefixByOldPrefix(process, prefix)+"_"; + } + String newId = element.getText().replace(prefix + ":", newPrefix); + LOGGER.debug("new prefix '{}' for ID '{}' - new ID: {}", newPrefix, element.getText(), newId); + element.setText(newId); + } private String getNewPrefixByOldPrefix(BPMNProcess process, @@ -148,12 +194,19 @@ private String getNewPrefixByOldPrefix(BPMNProcess process, */ private void renameIds(BPMNProcess importedProcess) { - List attributes = setupXPathReplaceIdsForAttributes().evaluate(importedProcess.getProcessAsDoc()); - List elements = setupXPathReplaceIdsForElements().evaluate( - importedProcess.getProcessAsDoc()); - - attributes.forEach(x -> x.setValue(importedProcess.getGeneratedPrefix()+"_"+x.getValue())); - elements.forEach(x -> x.setText(importedProcess.getGeneratedPrefix()+"_"+x.getText())); + List attributes = createXPathExpressionForAllIDsAndReferenceAttributes().evaluate(importedProcess.getProcessAsDoc()); + List elements = createXPathExpressionForAllReferenceElements().evaluate(importedProcess.getProcessAsDoc()); + + attributes.stream().filter(x -> !x.getValue().contains(":")) + .forEach(x -> { + LOGGER.debug("Updating attribute ID '{}' new value: {}", x.getValue(), importedProcess.getGeneratedPrefix()+"_"+x.getValue()); + x.setValue(importedProcess.getGeneratedPrefix()+"_"+x.getValue()); + }); + elements.stream().filter(x-> !x.getText().contains(":")) + .forEach(x -> { + LOGGER.debug("Updating element {} ID '{}' new value: {}", x.getName(), x.getText(), importedProcess.getGeneratedPrefix()+"_"+x.getText()); + x.setText(importedProcess.getGeneratedPrefix()+"_"+x.getText()); + }); } /** @@ -180,45 +233,43 @@ private void addNodesToDocument(Document importedProcess, } /** - * helper method to easily set up the namespace collecting for the renaming - * of the ids + * Creates an XPathExpression filtering all Attributes which are used to reference another BPMN element */ - private XPathExpression setupXPathNamespaceIdsForAttributes() { - String expStr = "//bpmn:*/@sourceRef | //bpmn:*/@targetRef | " - + "//bpmn:*/@calledElement | //bpmn:*/@processRef | " - + "//bpmn:*/@dataStoreRef | //bpmn:*/@categoryValueRef"; - return xPathFactory.compile(expStr, Filters.attribute(), null, + private XPathExpression createXPathExpressionForAllQNameAttributes() { + + return xPathFactory.compile(ALL_QNAME_ATTRIBUTES, Filters.attribute(), null, getBpmnNamespace()); } /** - * helper method to easily set up the namespace collecting for the renaming - * of the ids + * Creates an XPathExpression filtering all Elements which are used to reference another BPMN element */ - private XPathExpression setupXPathNamespaceIdsForElements() { - String expStr = "//bpmn:*/eventDefinitionRef"; - return xPathFactory.compile(expStr, Filters.element(), null, + private XPathExpression createXPathExpressionForAllQNameElements() { + + return xPathFactory.compile(ALL_QNAME_ELEMENTS, Filters.element(), null, getBpmnNamespace()); } /** - * helper method to easily set up the id collecting for the renaming of the - * ids + * Creates an XPathExpression filtering all Attributes which define and use IDs + * (regardless of the used type (ID, IDREF or QName)) */ - private XPathExpression setupXPathReplaceIdsForAttributes() { - String expStr = "//bpmn:*/@id | //bpmn:*/@sourceRef | //bpmn:*/@targetRef | " - + "//bpmn:*/@processRef | //bpmn:*/@dataStoreRef | " - + "//bpmn:*/@categoryValueRef"; + private XPathExpression createXPathExpressionForAllIDsAndReferenceAttributes() { + + String expStr = "//bpmn:*/@id | " + ALL_IDREF_ATTRIBUTES +" | "+ ALL_QNAME_ATTRIBUTES; return xPathFactory.compile(expStr, Filters.attribute(), null, getBpmnNamespace()); } - private XPathExpression setupXPathReplaceIdsForElements() { - String expStr = "//bpmn:*/eventDefinitionRef | " - + "//bpmn:incoming | //bpmn:outgoing | //bpmn:dataInputRefs | " - + "//bpmn:dataOutputRefs"; + /** + * Creates an XPathExpression filtering all Elements which use IDs + * (regardless of the used type (ID, IDREF or QName)) + */ + private XPathExpression createXPathExpressionForAllReferenceElements() { + + String expStr = ALL_IDREF_ELEMENTS + " | " + ALL_QNAME_ELEMENTS; return xPathFactory.compile(expStr, Filters.element(), null, getBpmnNamespace()); diff --git a/src/test/java/de/uniba/dsg/bpmnspector/schematron/PreProcessing.java b/src/test/java/de/uniba/dsg/bpmnspector/schematron/PreProcessing.java index fd17891..7bb3a16 100644 --- a/src/test/java/de/uniba/dsg/bpmnspector/schematron/PreProcessing.java +++ b/src/test/java/de/uniba/dsg/bpmnspector/schematron/PreProcessing.java @@ -1,7 +1,7 @@ package de.uniba.dsg.bpmnspector.schematron; -import api.ValidationResult; import api.ValidationException; +import api.ValidationResult; import api.Violation; import org.junit.Test; @@ -57,6 +57,12 @@ public void testConstraintParticipantImportedProcessFail() "//bpmn:*[@id = '_3']", 4); } + @Test + public void testTargetNamespaceIdReplacements() + throws ValidationException { + verifyValidResult(createFile("testTnsIdReplacement.bpmn")); + } + @Override protected void assertViolation(Violation v, String fileName, int line) { assertViolation(v, ERRORMESSAGE, fileName, XPATHSTRING, line); diff --git a/src/test/resources/preprocessing/testTnsIdReplacement.bpmn b/src/test/resources/preprocessing/testTnsIdReplacement.bpmn new file mode 100644 index 0000000..c29e286 --- /dev/null +++ b/src/test/resources/preprocessing/testTnsIdReplacement.bpmn @@ -0,0 +1,125 @@ + + + + + tns:_4 + _6 + + + _4 + tns:_8 + + + + _6 + _9 + + + + _8 + _9 + _11 + _13 + + + + + _11 + + + + _13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +