Skip to content

Commit

Permalink
Allow usage of namespaced QNames referencing targetNamespace - fixes #8
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasgeiger committed Oct 7, 2015
1 parent 2c81c11 commit 36db196
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
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;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class JDOMUtils {

Expand Down Expand Up @@ -83,4 +85,16 @@ private static void addElementsFromSingleDocument(
}
}

public static Optional<String> getUsedPrefixForTargetNamespace(Document document) {
Element rootElem = document.getRootElement();
String targetNamespaceURI = rootElem.getAttributeValue("targetNamespace");
Optional<Namespace> namespaceForTargetNamespace = rootElem.getNamespacesInScope().stream().filter(n -> n.getURI().equals(targetNamespaceURI)).findAny();
if (namespaceForTargetNamespace.isPresent()) {
return Optional.of(namespaceForTargetNamespace.get().getPrefix());
}
return Optional.empty();
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -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<Attribute> refAttributes = setupXPathNamespaceIdsForAttributes().evaluate(
cloneOfDoc);
List<Element> refElements = setupXPathNamespaceIdsForElements().evaluate(
cloneOfDoc);
List<Attribute> refAttributes = createXPathExpressionForAllQNameAttributes().evaluate(cloneOfDoc);
List<Element> refElements = createXPathExpressionForAllQNameElements().evaluate(cloneOfDoc);

// for each attribute: rename IDs which can be used globally
refAttributes.stream()
Expand Down Expand Up @@ -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);
}

/**
Expand All @@ -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,
Expand Down Expand Up @@ -148,12 +194,19 @@ private String getNewPrefixByOldPrefix(BPMNProcess process,
*/
private void renameIds(BPMNProcess importedProcess) {

List<Attribute> attributes = setupXPathReplaceIdsForAttributes().evaluate(importedProcess.getProcessAsDoc());
List<Element> elements = setupXPathReplaceIdsForElements().evaluate(
importedProcess.getProcessAsDoc());

attributes.forEach(x -> x.setValue(importedProcess.getGeneratedPrefix()+"_"+x.getValue()));
elements.forEach(x -> x.setText(importedProcess.getGeneratedPrefix()+"_"+x.getText()));
List<Attribute> attributes = createXPathExpressionForAllIDsAndReferenceAttributes().evaluate(importedProcess.getProcessAsDoc());
List<Element> 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());
});
}

/**
Expand All @@ -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<Attribute> setupXPathNamespaceIdsForAttributes() {
String expStr = "//bpmn:*/@sourceRef | //bpmn:*/@targetRef | "
+ "//bpmn:*/@calledElement | //bpmn:*/@processRef | "
+ "//bpmn:*/@dataStoreRef | //bpmn:*/@categoryValueRef";
return xPathFactory.compile(expStr, Filters.attribute(), null,
private XPathExpression<Attribute> 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<Element> setupXPathNamespaceIdsForElements() {
String expStr = "//bpmn:*/eventDefinitionRef";
return xPathFactory.compile(expStr, Filters.element(), null,
private XPathExpression<Element> 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<Attribute> setupXPathReplaceIdsForAttributes() {
String expStr = "//bpmn:*/@id | //bpmn:*/@sourceRef | //bpmn:*/@targetRef | "
+ "//bpmn:*/@processRef | //bpmn:*/@dataStoreRef | "
+ "//bpmn:*/@categoryValueRef";
private XPathExpression<Attribute> createXPathExpressionForAllIDsAndReferenceAttributes() {

String expStr = "//bpmn:*/@id | " + ALL_IDREF_ATTRIBUTES +" | "+ ALL_QNAME_ATTRIBUTES;

return xPathFactory.compile(expStr, Filters.attribute(), null,
getBpmnNamespace());
}

private XPathExpression<Element> 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<Element> createXPathExpressionForAllReferenceElements() {

String expStr = ALL_IDREF_ELEMENTS + " | " + ALL_QNAME_ELEMENTS;

return xPathFactory.compile(expStr, Filters.element(), null,
getBpmnNamespace());
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 36db196

Please sign in to comment.