blob: c9cd9e01c0b5d2a0ae57c7e881290c16fdc4cee6 [file] [log] [blame]
/**
* Copyright (c) 2010 Henning Heitkoetter.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Henning Heitkoetter - initial API and implementation
*/
package org.eclipse.bpmn2.tests;
import static org.junit.Assert.*;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import org.eclipse.bpmn2.BaseElement;
import org.eclipse.bpmn2.Bpmn2Factory;
import org.eclipse.bpmn2.Collaboration;
import org.eclipse.bpmn2.Definitions;
import org.eclipse.bpmn2.DocumentRoot;
import org.eclipse.bpmn2.EventDefinition;
import org.eclipse.bpmn2.Import;
import org.eclipse.bpmn2.LinkEventDefinition;
import org.eclipse.bpmn2.Process;
import org.eclipse.bpmn2.StartEvent;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xmi.ClassNotFoundException;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* Tests our QName (de)resolution mechanism both for intra- and inter-model references.
*
* This test uses XML serialization (no QNames in XMI).
* @author Henning Heitkoetter
*
*/
public class QNameReferenceTest extends Bpmn2SerializationTest {
protected Definitions A_model, B_model;
protected Collaboration A_collab, B_collab;
protected Process A_process, B_process;
protected Resource A_resource, B_resource;
@Before
public void setUpModels() {
A_model = TestHelper.initBasicModel("urn:modelA");
B_model = TestHelper.initBasicModel("urn:modelB");
A_collab = Bpmn2Factory.eINSTANCE.createCollaboration();
A_model.getRootElements().add(A_collab);
A_process = Bpmn2Factory.eINSTANCE.createProcess();
A_model.getRootElements().add(A_process);
B_collab = Bpmn2Factory.eINSTANCE.createCollaboration();
B_model.getRootElements().add(B_collab);
B_process = Bpmn2Factory.eINSTANCE.createProcess();
B_model.getRootElements().add(B_process);
}
@Override
protected String getFileExtension() {
return EXTENSION_BPMN2_XML;
}
@Override
protected String getSubDirectory() {
return "qname";
}
/**
* Saves both models (A and B). Creates a import element in A pointing to B.
* Loads the created resources into {@link #A_resource} and {@link #B_resource}, respectively.
*
* @param name Part of the file name that is used to identify different tests.
* @throws IOException
*/
protected void saveAndLoadModels(String name) throws IOException {
Map<URI, EObject> uriToContentMap = new LinkedHashMap<URI, EObject>();
URI A_fileUri = URI.createFileURI("tmp/" + getSubDirectory() + "/" + name + "_A."
+ getFileExtension());
URI B_fileUri = URI.createFileURI("tmp/" + getSubDirectory() + "/" + name + "_B."
+ getFileExtension());
uriToContentMap.put(A_fileUri, A_model);
uriToContentMap.put(B_fileUri, B_model);
Import importBintoA = Bpmn2Factory.eINSTANCE.createImport();
importBintoA.setImportType("http://www.omg.org/spec/BPMN/20100524/MODEL");
importBintoA.setLocation(B_fileUri.toString());
importBintoA.setNamespace("urn:modelB");
A_model.getImports().add(importBintoA);
TestHelper.createResourcesWithContent(uriToContentMap);
createdFiles.add(A_fileUri);
createdFiles.add(B_fileUri);
A_resource = TestHelper.getResource(A_fileUri);
B_resource = TestHelper.getResource(B_fileUri);
}
/**
* Asserts that obj can be resolved and resolves to expected.
* @param message Message if obj is a proxy (not resolved).
* @param obj The obj to check.
* @param expected obj is expected to be equivalent to this parameter, as
* determined by their {@linkplain BaseElement#getId() ID}.
*/
protected static void assertResolvesTo(String message, BaseElement obj, BaseElement expected) {
assertFalse(message, obj.eIsProxy());
assertEquals(expected.getId(), obj.getId());
}
@Test
public void testIntraModelReferencesRelative() throws Exception {
testIntraModelReferences(false);
}
@Test
public void testIntraModelReferencesAbsolute() throws Exception {
testIntraModelReferences(true);
}
/**
* Test proxy-resolving references (i.e. potentially cross-file references) when
* they point to objects within the same file.
* @param absolute Determines if the model is saved and loaded with an absolute or relative URI.
* @throws IOException
*/
public void testIntraModelReferences(boolean absolute) throws IOException {
// single @ XML attribute
A_process.setDefinitionalCollaborationRef(A_collab);
// many @ XML attribute - no cross-file reference available
// single @ XML element
LinkEventDefinition A_led1, A_led2;
A_led1 = Bpmn2Factory.eINSTANCE.createLinkEventDefinition();
A_model.getRootElements().add(A_led1);
A_led2 = Bpmn2Factory.eINSTANCE.createLinkEventDefinition();
A_model.getRootElements().add(A_led2);
A_led1.setTarget(A_led2);
// many @ XML element
Process A_process2 = Bpmn2Factory.eINSTANCE.createProcess();
A_model.getRootElements().add(A_process2);
A_process.getSupports().add(A_process2);
Resource res = saveAndLoadModel("intra", A_model, absolute);
Process A_processNew = (Process) res.getEObject(A_process.getId());
LinkEventDefinition A_led1New = (LinkEventDefinition) res.getEObject(A_led1.getId());
assertResolvesTo("Proxy could not be resolved (single attr)",
A_processNew.getDefinitionalCollaborationRef(), A_collab);
assertResolvesTo("Proxy could not be resolved (single elem)", A_led1New.getTarget(), A_led2);
assertResolvesTo("Proxy could not be resolved (many elem)",
A_processNew.getSupports().get(0), A_process2);
}
/**
* Test a proxy-resolving intra-file reference with an abstract type.
* @throws Exception
*/
@Test
public void testReferenceToAbstractType() throws Exception {
EventDefinition A_eventDef = Bpmn2Factory.eINSTANCE.createSignalEventDefinition();
A_model.getRootElements().add(A_eventDef);
StartEvent A_event = Bpmn2Factory.eINSTANCE.createStartEvent();
A_event.getEventDefinitionRefs().add(A_eventDef);
A_process.getFlowElements().add(A_event);
try {
Resource res = saveAndLoadModel("toAbstract", A_model);
StartEvent A_eventNew = (StartEvent) res.getEObject(A_event.getId());
assertResolvesTo("Proxy could not be resolved (abstract)", A_eventNew
.getEventDefinitionRefs().get(0), A_eventDef);
} catch (WrappedException e) {
if (e.exception() instanceof ClassNotFoundException)
fail("Class EventDefinition was recognized as abstract.");
else
throw e;
} catch (IllegalArgumentException e) {
// different error in Eclipse 3.4
if (e.getMessage().endsWith("not a valid classifier"))
fail("Class EventDefinition was recognized as abstract.");
else
throw e;
}
}
/**
* Test cross-file references.
* @throws IOException
*/
@Test
public void testInterModelReferences() throws IOException {
// single @ XML attribute
A_process.setDefinitionalCollaborationRef(B_collab);
// many @ XML attribute - no cross-file reference available
// single @ XML element
LinkEventDefinition A_led, B_led;
A_led = Bpmn2Factory.eINSTANCE.createLinkEventDefinition();
A_model.getRootElements().add(A_led);
B_led = Bpmn2Factory.eINSTANCE.createLinkEventDefinition();
B_model.getRootElements().add(B_led);
A_led.setTarget(B_led);
// many @ XML element
A_process.getSupports().add(B_process);
saveAndLoadModels("inter");
Process A_processNew = (Process) A_resource.getEObject(A_process.getId());
LinkEventDefinition A_ledNew = (LinkEventDefinition) A_resource.getEObject(A_led.getId());
assertResolvesTo("Proxy could not be resolved (single attr)",
A_processNew.getDefinitionalCollaborationRef(), B_collab);
assertResolvesTo("Proxy could not be resolved (single elem)", A_ledNew.getTarget(), B_led);
assertResolvesTo("Proxy could not be resolved (many elem)",
A_processNew.getSupports().get(0), B_process);
}
@Test
public void testQNameSerialization() throws Exception {
A_process.setId("A_process_id");
A_process.setDefinitionalCollaborationRef(A_collab);
A_process.getSupports().add(B_process);
saveAndLoadModels("serialization");
DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
Document xml = fact.newDocumentBuilder()
.parse(new File(A_resource.getURI().toFileString()));
Node pNode = xml.getElementsByTagName("bpmn2:process").item(0);
String ref = pNode.getAttributes().getNamedItem("definitionalCollaborationRef")
.getNodeValue();
String prefixLocal = ref.indexOf(":") == -1 ? "" : ref.split(":")[0];
assertEquals("The prefix for local references is not mapped to the target namespace.",
A_model.getTargetNamespace(), ((DocumentRoot) A_resource.getContents().get(0))
.getXMLNSPrefixMap().get(prefixLocal));
ref = pNode.getChildNodes().item(1).getTextContent();
String prefixModelB = ref.indexOf(":") == -1 ? "" : ref.split(":")[0];
assertEquals("The prefix for references to model b is not mapped to its target namespace.",
B_model.getTargetNamespace(), ((DocumentRoot) A_resource.getContents().get(0))
.getXMLNSPrefixMap().get(prefixModelB));
}
/**
* Tests the standard case for the mapping of prefixes to namespaces:
* - the default namespace is set to the target namespace and local references do not use a prefix
* - cross-file references use a prefix associated with an imported namespace
*/
@Test
public void testPrefixNamespaceMapping() {
testQNameResolve("normal", true);
}
/**
* Tests the inverse case for the mapping of prefixes to namespaces:
* - an explicit prefix is associated with the target namespace and used in local references
* - the default namespace point to an imported namespace and cross-file reference do not use a prefix
*/
@Test
public void testPrefixNamespaceMappingInverse() {
testQNameResolve("inverse", true);
}
/**
* Tests the special case for the mapping of prefixes to namespaces:
* - neither a default namespace nor a target namespace are defined, unprefixed QNames thus point to local elements
*/
@Test
public void testPrefixNamespaceMappingNoNs() {
testQNameResolve("noNs", false);
}
/**
* Tests the special case for the mapping of prefixes to namespaces:
* - no default namespace but a target namespace is defined, unprefixed QNames are invalid (strict interpretation)
* - chosen fallback behavior: unprefixed QNames should be interpreted as references to local elements
*/
@Test
public void testPrefixNamespaceMappingNoDefaultButTarget() {
testQNameResolve("noDefaultButTarget", false);
}
/**
* Tests the special case for the mapping of prefixes to namespaces:
* - the default namespace is neither the target namespace nor mapped by an import element
* - strict interpretation: unprefixed QNames can not be resolved
* - chosen fallback behavior (?): unprefixed QNames should be interpreted as references to local elements
* -- guideline: treat unprefixed QNames as local (guess), unless default namespace is mapped by import element
*/
@Test
public void testPrefixNamespaceMappingDefaultNotResolvable() {
testQNameResolve("defaultNotResolvable", true);
}
/**
* Tests if QNames are correctly resolved in the specified test case.
* @param testCase Identifier of the test case, pointing to a BPMN2 file
* under 'res/qname_prefixNs_<i>testCase</i>.bpmn2' (with defined content).
* @param crossFile <code>true</code>, if this test case features a cross-file reference
* to be checked (Process.supports).
*/
protected void testQNameResolve(String testCase, boolean crossFile) {
Resource res = TestHelper.getResource(URI.createFileURI("res/qname_prefixNs_" + testCase
+ ".bpmn2"));
Process p = (Process) res.getEObject("proc1Id");
assertFalse("Resolution of local reference failed, test case: " + testCase, p
.getDefinitionalCollaborationRef().eIsProxy());
if (crossFile)
assertFalse("Resolution of cross-file reference failed, test case: " + testCase, p
.getSupports().get(0).eIsProxy());
}
/**
* Tests the automated generation of prefixes in case multiple imports would use the same prefix (as
* automatically generated from the namespace).
* @throws Exception
*/
@Test
public void testPrefixClash() throws Exception {
Definitions C_model = TestHelper.initBasicModel("urn:path:modelB"); // same ns ending as B_model
Process C_process = Bpmn2Factory.eINSTANCE.createProcess();
C_model.getRootElements().add(C_process);
Resource C_resource = saveAndLoadModel("prefixClash_C", C_model);
Import importCintoA = Bpmn2Factory.eINSTANCE.createImport();
importCintoA.setImportType("http://www.omg.org/spec/BPMN/20100524/MODEL");
importCintoA.setLocation(C_resource.getURI().toString());
importCintoA.setNamespace(C_model.getTargetNamespace());
A_model.getImports().add(importCintoA);
A_process.getSupports().add(B_process);
A_process.getSupports().add(C_process);
saveAndLoadModels("prefixClash");
Process A_processNew = (Process) A_resource.getEObject(A_process.getId());
assertResolvesTo("Reference to element of model B could not be resolved", A_processNew
.getSupports().get(0), B_process);
assertResolvesTo("Reference to element of model C could not be resolved", A_processNew
.getSupports().get(1), C_process);
}
}