Support uml model linking

- Essentially add support to be able to understand
  papyrus uml linked models.
- Change handling of files in a classpath(fat-jar) to copy
  those into jvm tmp directory in a structure where relative
  links can work.
- Forward port #888
This commit is contained in:
Janne Valkealahti
2020-12-05 13:36:46 +00:00
parent 943346e182
commit f4ce9310c1
14 changed files with 682 additions and 73 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

View File

@@ -2723,6 +2723,27 @@ sub-state.
image::images/papyrus-gs-15.png[scaledwidth="100%"]
[[sm-papyrus-import]]
=== Using a Machine Import
It's also possible to use import functionality where uml files can reference to other models.
image::images/papyrus-gs-22.png[scaledwidth="100%"]
Within `UmlStateMachineModelFactory` it's possible to use additional resources or locations
to define referenced model files.
====
[source,java,indent=0]
----
include::samples/DocsUmlSampleTests1.java[tags=snippetC]
----
====
IMPORTANT: Links between files in uml models needs to be relative as
otherwise things break when model files are copied out from a
classpath to a temporary directory so that eclipse parsing classes can
read those.
[[sm-repository]]
== Repository Support

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed 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
*
* https://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.
*/
package org.springframework.statemachine.uml;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.stream.Stream;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.statemachine.StateMachineException;
import org.springframework.util.FileCopyUtils;
/**
* Support class to resolve uml resources and handling a cases where things
* needs to be copied away from a classpath trying to handle common relative
* paths with a locations or resource paths in case of a cross relative linking.
*
* @author Janne Valkealahti
*/
class ResourcerResolver {
private ResourceLoader resourceLoader;
private Resource mainResource;
private String mainLocation;
private Resource[] additionalResources;
private String[] additionalLocations;
public ResourcerResolver(Resource mainResource, Resource[] additionalResources) {
this.mainResource = mainResource;
this.additionalResources = additionalResources != null ? additionalResources : new Resource[0];
}
public ResourcerResolver(ResourceLoader resourceLoader, String mainLocation, String[] additionalLocations) {
this.resourceLoader = resourceLoader;
this.mainLocation = mainLocation;
this.additionalLocations = additionalLocations != null ? additionalLocations : new String[0];
}
public Holder[] resolve() {
ArrayList<Holder> holders = new ArrayList<>();
if (mainLocation != null) {
Resource[] resources = Stream.concat(Stream.of(mainLocation), Stream.of(additionalLocations))
.map(location -> resourceLoader.getResource(location))
.toArray(Resource[]::new);
return getResourceUris(resources);
} else if (mainResource != null) {
Resource[] resources = Stream.concat(Stream.of(mainResource), Stream.of(additionalResources))
.toArray(Resource[]::new);
return getResourceUris(resources);
}
return holders.toArray(new Holder[0]);
}
private Holder[] getResourceUris(Resource... resources) {
ArrayList<Holder> holders = new ArrayList<>();
try {
if (allPhysical(resources)) {
for (Resource resource : resources) {
holders.add(new Holder(resource.getFile().toURI()));
}
} else {
Path tempDir = Files.createTempDirectory(null);
for (Resource resource : resources) {
if (resource instanceof ClassPathResource) {
ClassPathResource cpr = (ClassPathResource)resource;
File f = new File(tempDir.toFile(), cpr.getPath());
f.getParentFile().mkdirs();
FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(f));
holders.add(new Holder(f.toURI(), f.toPath()));
}
}
}
} catch (IOException e) {
throw new StateMachineException(e);
}
return holders.toArray(new Holder[0]);
}
private boolean isPhysical(Resource resource) {
try {
resource.getFile();
return true;
} catch (Exception e) {
}
return false;
}
private boolean allPhysical(Resource... resources) {
for (Resource resource : resources) {
if (!isPhysical(resource)) {
return false;
}
}
return true;
}
static class Holder {
URI uri;
Path path;
public Holder(URI uri) {
this(uri, null);
}
public Holder(URI uri, Path path) {
this.uri = uri;
this.path = path;
}
public URI getUri() {
return uri;
}
public Path getPath() {
return path;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2018 the original author or authors.
* Copyright 2016-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,11 +15,7 @@
*/
package org.springframework.statemachine.uml;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.uml2.uml.Model;
@@ -29,23 +25,33 @@ import org.springframework.statemachine.config.model.AbstractStateMachineModelFa
import org.springframework.statemachine.config.model.DefaultStateMachineModel;
import org.springframework.statemachine.config.model.StateMachineModel;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
import org.springframework.statemachine.uml.ResourcerResolver.Holder;
import org.springframework.statemachine.uml.support.UmlModelParser;
import org.springframework.statemachine.uml.support.UmlModelParser.DataHolder;
import org.springframework.statemachine.uml.support.UmlUtils;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
/**
* {@link StateMachineModelFactory} which builds {@link StateMachineModel} from
* uml representation.
*
* {@code resource} or {@code location} is a main uml file used as a source
* passed to parser classes. {@code additionalResources} and {@code additionalLocations}
* are needed if uml model have references or links to additional uml files as an
* import. In case of a these files being located in a classpath which is inside of
* a jar, files are copied out into filesystem as eclipse uml libs can only parse
* physical files. In a case of this a common "path" from all resources are resolved
* and copied into filesystem with a structure so that at least relative links in uml
* files will work.
*
* @author Janne Valkealahti
*/
public class UmlStateMachineModelFactory extends AbstractStateMachineModelFactory<String, String>
implements StateMachineModelFactory<String, String> {
public class UmlStateMachineModelFactory extends AbstractStateMachineModelFactory<String, String> {
private Resource resource;
private String location;
private Resource[] additionalResources;
private String[] additionalLocations;
/**
* Instantiates a new uml state machine model factory.
@@ -53,8 +59,7 @@ public class UmlStateMachineModelFactory extends AbstractStateMachineModelFactor
* @param resource the resource
*/
public UmlStateMachineModelFactory(Resource resource) {
Assert.notNull(resource, "Resource must be set");
this.resource = resource;
this(resource, null);
}
/**
@@ -63,20 +68,51 @@ public class UmlStateMachineModelFactory extends AbstractStateMachineModelFactor
* @param location the resource location
*/
public UmlStateMachineModelFactory(String location) {
this(location, null);
}
/**
* Instantiates a new uml state machine model factory.
*
* @param resource the resource
* @param additionalResources the additional resources
*/
public UmlStateMachineModelFactory(Resource resource, Resource[] additionalResources) {
Assert.notNull(resource, "Resource must be set");
this.resource = resource;
this.additionalResources = additionalResources;
}
/**
* Instantiates a new uml state machine model factory.
*
* @param location the resource location
* @param additionalLocations the additional locations
*/
public UmlStateMachineModelFactory(String location, String[] additionalLocations) {
Assert.notNull(location, "Location must be set");
this.location = location;
this.additionalLocations = additionalLocations;
}
@Override
public StateMachineModel<String, String> build() {
Model model = null;
ResourcerResolver resourceResolver = null;
if (this.location != null) {
resourceResolver = new ResourcerResolver(getResourceLoader(), location, additionalLocations);
} else if (this.resource != null) {
resourceResolver = new ResourcerResolver(resource, additionalResources);
}
Holder holder = null;
Model model = null;
org.eclipse.emf.ecore.resource.Resource resource = null;
try {
holder = getResourceUri(resolveResource());
Holder[] resources = resourceResolver.resolve();
holder = resources != null && resources.length > 0 ? resources[0] : null;
resource = UmlUtils.getResource(holder.uri.getPath());
model = (Model) EcoreUtil.getObjectByType(resource.getContents(), UMLPackage.Literals.MODEL);
} catch (IOException e) {
} catch (Exception e) {
throw new IllegalArgumentException("Cannot build build model from resource " + resource + " or location " + location, e);
} finally {
// if we have a path, tmp file were created, clean it
@@ -87,6 +123,7 @@ public class UmlStateMachineModelFactory extends AbstractStateMachineModelFactor
}
}
}
UmlModelParser parser = new UmlModelParser(model, this);
DataHolder dataHolder = parser.parseModel();
@@ -107,39 +144,4 @@ public class UmlStateMachineModelFactory extends AbstractStateMachineModelFactor
// we don't set configurationData here, so assume null
return new DefaultStateMachineModel<String, String>(null, dataHolder.getStatesData(), dataHolder.getTransitionsData());
}
private Resource resolveResource() {
if (resource != null) {
return resource;
} else {
return getResourceLoader().getResource(location);
}
}
private Holder getResourceUri(Resource resource) throws IOException {
// try to see if resource is an actual File and eclipse
// libs cannot use input stream. thus creating a tmp file with
// needed .uml prefix and getting URI from there.
try {
return new Holder(resource.getFile().toURI());
} catch (Exception e) {
}
Path tempFile = Files.createTempFile(null, ".uml");
FileCopyUtils.copy(resource.getInputStream(), new FileOutputStream(tempFile.toFile()));
return new Holder(tempFile.toUri(), tempFile);
}
private static class Holder {
URI uri;
Path path;
public Holder(URI uri) {
this(uri, null);
}
public Holder(URI uri, Path path) {
this.uri = uri;
this.path = path;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 the original author or authors.
* Copyright 2016-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -69,8 +69,9 @@ import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Model parser which constructs states and transitions data out from
* an uml model.
* Model parser which constructs states and transitions data out from an uml
* model. This implementation is not thread safe and model parsing can only be
* used once per instance.
*
* @author Janne Valkealahti
*/
@@ -92,6 +93,10 @@ public class UmlModelParser {
private final AtomicInteger pseudostateNamingCounter = new AtomicInteger(1);
private final Map<NamedElement, String> pseudostateNaming = new HashMap<>();
private final List<String> seenStateData = new ArrayList<>();
private final List<String> seenEntryData = new ArrayList<>();
private final List<String> seenExitData = new ArrayList<>();
private final List<String> seenTransitionData = new ArrayList<>();
/**
* Instantiates a new uml model parser.
@@ -158,6 +163,46 @@ public class UmlModelParser {
});
}
private void addStateData(StateData<String, String> stateData) {
String key = stateData.getState();
if (!seenStateData.contains(key)) {
stateDatas.add(stateData);
seenStateData.add(key);
}
}
private void addEntryData(EntryData<String, String> entryData) {
String skey = entryData.getSource() != null ? entryData.getSource() : "null";
String tkey = entryData.getTarget() != null ? entryData.getTarget() : "null";
String key = skey + "_" + tkey;
if (!seenEntryData.contains(key)) {
entrys.add(entryData);
seenEntryData.add(key);
}
}
private void addExitData(ExitData<String, String> exitData) {
String skey = exitData.getSource() != null ? exitData.getSource() : "null";
String tkey = exitData.getTarget() != null ? exitData.getTarget() : "null";
String key = skey + "_" + tkey;
if (!seenExitData.contains(key)) {
exits.add(exitData);
seenExitData.add(key);
}
}
private void addTransitionData(TransitionData<String, String> transitionData) {
String skey = transitionData.getSource() != null ? transitionData.getSource() : "null";
String tkey = transitionData.getTarget() != null ? transitionData.getTarget() : "null";
String ekey = transitionData.getEvent() != null ? transitionData.getEvent() : "null";
String kkey = transitionData.getKind() != null ? transitionData.getKind().toString() : "null";
String key = skey + "_" + tkey + "_" + ekey + "_" + kkey;
if (!seenTransitionData.contains(key)) {
transitionDatas.add(transitionData);
seenTransitionData.add(key);
}
}
private void handleRegion(Region region) {
// build states
for (Vertex vertex : region.getSubvertices()) {
@@ -192,7 +237,7 @@ public class UmlModelParser {
if (UmlUtils.isFinalState(state)) {
stateData.setEnd(true);
}
stateDatas.add(stateData);
addStateData(stateData);
// add states via entry/exit reference points
for (ConnectionPointReference cpr : state.getConnections()) {
@@ -200,14 +245,14 @@ public class UmlModelParser {
for (Pseudostate cp : cpr.getEntries()) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, cp.getName(), false);
cpStateData.setPseudoStateKind(PseudoStateKind.ENTRY);
stateDatas.add(cpStateData);
addStateData(cpStateData);
}
}
if (cpr.getExits() != null) {
for (Pseudostate cp : cpr.getExits()) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, cp.getName(), false);
cpStateData.setPseudoStateKind(PseudoStateKind.EXIT);
stateDatas.add(cpStateData);
addStateData(cpStateData);
}
}
}
@@ -223,13 +268,18 @@ public class UmlModelParser {
if (kind != null) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, cp.getName(), false);
cpStateData.setPseudoStateKind(kind);
stateDatas.add(cpStateData);
addStateData(cpStateData);
}
}
// do recursive handling of regions
for (Region sub : state.getRegions()) {
handleRegion(sub);
if (!state.getRegions().isEmpty()) {
// do recursive handling of regions
for (Region sub : state.getRegions()) {
handleRegion(sub);
}
} else if (state.getSubmachine() != null) {
// submachine would be there i.e. with import
handleStateMachine(state.getSubmachine());
}
}
// pseudostates like choice, etc
@@ -256,27 +306,27 @@ public class UmlModelParser {
if (state.getKind() == PseudostateKind.CHOICE_LITERAL) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, resolveName(state), false);
cpStateData.setPseudoStateKind(PseudoStateKind.CHOICE);
stateDatas.add(cpStateData);
addStateData(cpStateData);
} else if (state.getKind() == PseudostateKind.JUNCTION_LITERAL) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, state.getName(), false);
cpStateData.setPseudoStateKind(PseudoStateKind.JUNCTION);
stateDatas.add(cpStateData);
addStateData(cpStateData);
} else if (state.getKind() == PseudostateKind.FORK_LITERAL) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, state.getName(), false);
cpStateData.setPseudoStateKind(PseudoStateKind.FORK);
stateDatas.add(cpStateData);
addStateData(cpStateData);
} else if (state.getKind() == PseudostateKind.JOIN_LITERAL) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, state.getName(), false);
cpStateData.setPseudoStateKind(PseudoStateKind.JOIN);
stateDatas.add(cpStateData);
addStateData(cpStateData);
} else if (state.getKind() == PseudostateKind.SHALLOW_HISTORY_LITERAL) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, state.getName(), false);
cpStateData.setPseudoStateKind(PseudoStateKind.HISTORY_SHALLOW);
stateDatas.add(cpStateData);
addStateData(cpStateData);
} else if (state.getKind() == PseudostateKind.DEEP_HISTORY_LITERAL) {
StateData<String, String> cpStateData = new StateData<>(parent, regionId, state.getName(), false);
cpStateData.setPseudoStateKind(PseudoStateKind.HISTORY_DEEP);
stateDatas.add(cpStateData);
addStateData(cpStateData);
}
}
}
@@ -296,19 +346,19 @@ public class UmlModelParser {
// realistic with state machines
EList<Pseudostate> cprentries = ((ConnectionPointReference)transition.getSource()).getEntries();
if (cprentries != null && cprentries.size() == 1 && cprentries.get(0).getKind() == PseudostateKind.ENTRY_POINT_LITERAL) {
entrys.add(new EntryData<String, String>(cprentries.get(0).getName(), resolveName(transition.getTarget())));
addEntryData(new EntryData<String, String>(cprentries.get(0).getName(), resolveName(transition.getTarget())));
}
EList<Pseudostate> cprexits = ((ConnectionPointReference)transition.getSource()).getExits();
if (cprexits != null && cprexits.size() == 1 && cprexits.get(0).getKind() == PseudostateKind.EXIT_POINT_LITERAL) {
exits.add(new ExitData<String, String>(cprexits.get(0).getName(), resolveName(transition.getTarget())));
addExitData(new ExitData<String, String>(cprexits.get(0).getName(), resolveName(transition.getTarget())));
}
}
if (transition.getSource() instanceof Pseudostate) {
if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.ENTRY_POINT_LITERAL) {
entrys.add(new EntryData<String, String>(resolveName(transition.getSource()), resolveName(transition.getTarget())));
addEntryData(new EntryData<String, String>(resolveName(transition.getSource()), resolveName(transition.getTarget())));
} else if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.EXIT_POINT_LITERAL) {
exits.add(new ExitData<String, String>(resolveName(transition.getSource()), resolveName(transition.getTarget())));
addExitData(new ExitData<String, String>(resolveName(transition.getSource()), resolveName(transition.getTarget())));
} else if (((Pseudostate)transition.getSource()).getKind() == PseudostateKind.CHOICE_LITERAL) {
LinkedList<ChoiceData<String, String>> list = choices.get(resolveName(transition.getSource()));
if (list == null) {
@@ -373,12 +423,12 @@ public class UmlModelParser {
if (transition.getTarget() instanceof ConnectionPointReference) {
EList<Pseudostate> cprentries = ((ConnectionPointReference)transition.getTarget()).getEntries();
if (cprentries != null && cprentries.size() == 1) {
transitionDatas.add(new TransitionData<String, String>(resolveName(transition.getSource()),
addTransitionData(new TransitionData<String, String>(resolveName(transition.getSource()),
cprentries.get(0).getName(), signal.getName(), UmlUtils.resolveTransitionActions(transition, resolver),
guard, UmlUtils.mapUmlTransitionType(transition)));
}
} else {
transitionDatas.add(new TransitionData<String, String>(resolveName(transition.getSource()),
addTransitionData(new TransitionData<String, String>(resolveName(transition.getSource()),
resolveName(transition.getTarget()), signal.getName(), UmlUtils.resolveTransitionActions(transition, resolver),
guard, UmlUtils.mapUmlTransitionType(transition)));
}
@@ -391,7 +441,7 @@ public class UmlModelParser {
if (timeEvent.isRelative()) {
count = 1;
}
transitionDatas.add(new TransitionData<String, String>(resolveName(transition.getSource()),
addTransitionData(new TransitionData<String, String>(resolveName(transition.getSource()),
resolveName(transition.getTarget()), period, count, UmlUtils.resolveTransitionActions(transition, resolver),
guard, UmlUtils.mapUmlTransitionType(transition)));
}
@@ -400,7 +450,7 @@ public class UmlModelParser {
// create anonymous transition if needed
if (shouldCreateAnonymousTransition(transition)) {
transitionDatas.add(new TransitionData<String, String>(resolveName(transition.getSource()), resolveName(transition.getTarget()),
addTransitionData(new TransitionData<String, String>(resolveName(transition.getSource()), resolveName(transition.getTarget()),
null, UmlUtils.resolveTransitionActions(transition, resolver), resolveGuard(transition),
UmlUtils.mapUmlTransitionType(transition)));
}

View File

@@ -0,0 +1,124 @@
/*
* Copyright 2020 the original author or authors.
*
* Licensed 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
*
* https://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.
*/
package org.springframework.statemachine.uml;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.statemachine.uml.ResourcerResolver.Holder;
public class ResourcerResolverTests {
private ResourceLoader resourceLoader = new DefaultResourceLoader();
// private String[] EMPTY_LOCATIONS = new String[0];
// in a below tests,
// we expect resources to resolve as a physical files
@Test
public void testOneMainLocation() throws Exception {
String mainLocation = "classpath:org/springframework/statemachine/uml/import-main/import-main.uml";
ResourcerResolver resolver = new ResourcerResolver(resourceLoader, mainLocation, null);
Holder[] resolved = resolver.resolve();
assertThat(resolved, notNullValue());
assertThat(resolved.length, is(1));
assertThat(resolved[0].getUri().getPath(), notNullValue());
}
@Test
public void testOneMainResource() throws Exception {
Resource mainResource = new ClassPathResource("org/springframework/statemachine/uml/import-main/import-main.uml");
ResourcerResolver resolver = new ResourcerResolver(mainResource, null);
Holder[] resolved = resolver.resolve();
assertThat(resolved, notNullValue());
assertThat(resolved.length, is(1));
assertThat(resolved[0].getUri().getPath(), notNullValue());
}
@Test
public void testMultipleLocations() throws Exception {
String mainLocation = "classpath:org/springframework/statemachine/uml/import-main/import-main.uml";
String subLocation = "classpath:org/springframework/statemachine/uml/import-sub/import-sub.uml";
ResourcerResolver resolver = new ResourcerResolver(resourceLoader, mainLocation, new String[]{subLocation});
Holder[] resolved = resolver.resolve();
assertThat(resolved, notNullValue());
assertThat(resolved.length, is(2));
assertThat(resolved[0].getUri().getPath(), notNullValue());
assertThat(resolved[1].getUri().getPath(), notNullValue());
}
@Test
public void testMultipleResources() throws Exception {
Resource mainResource = new ClassPathResource("org/springframework/statemachine/uml/import-main/import-main.uml");
Resource subResource = new ClassPathResource("org/springframework/statemachine/uml/import-sub/import-sub.uml");
ResourcerResolver resolver = new ResourcerResolver(mainResource, new Resource[]{subResource});
Holder[] resolved = resolver.resolve();
assertThat(resolved, notNullValue());
assertThat(resolved.length, is(2));
assertThat(resolved[0].getUri().getPath(), notNullValue());
assertThat(resolved[1].getUri().getPath(), notNullValue());
}
// in a below tests,
// do monkey thing and setup resource which will not resolve to
// an actual file as would happen with file inside boot fat-jar classpath
@Test
public void testOneMainResourceNotPhysicalFile() throws Exception {
Resource mainResource = new TestClassPathResource("org/springframework/statemachine/uml/import-main/import-main.uml");
ResourcerResolver resolver = new ResourcerResolver(mainResource, null);
Holder[] resolved = resolver.resolve();
assertThat(resolved, notNullValue());
assertThat(resolved.length, is(1));
assertThat(resolved[0].getUri().getPath(), notNullValue());
assertThat(resolved[0].getPath().toFile().exists(), is(true));
}
@Test
public void testMultipleResourcesNotPhysicalFile() throws Exception {
Resource mainResource = new TestClassPathResource("org/springframework/statemachine/uml/import-main/import-main.uml");
Resource subResource = new TestClassPathResource("org/springframework/statemachine/uml/import-sub/import-sub.uml");
ResourcerResolver resolver = new ResourcerResolver(mainResource, new Resource[]{subResource});
Holder[] resolved = resolver.resolve();
assertThat(resolved, notNullValue());
assertThat(resolved.length, is(2));
assertThat(resolved[0].getUri().getPath(), notNullValue());
assertThat(resolved[0].getPath().toFile().exists(), is(true));
assertThat(resolved[1].getUri().getPath(), notNullValue());
assertThat(resolved[1].getPath().toFile().exists(), is(true));
}
private static class TestClassPathResource extends ClassPathResource {
public TestClassPathResource(String path) {
super(path);
}
@Override
public File getFile() throws IOException {
throw new IOException();
}
}
}

View File

@@ -1136,6 +1136,17 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests {
assertThat(stateMachine.getExtendedState().get("key", String.class), is("value"));
}
@Test
@SuppressWarnings("unchecked")
public void testImportedSubMachine() {
context.register(Config28.class);
context.refresh();
StateMachine<String, String> stateMachine = context.getBean(StateMachine.class);
stateMachine.start();
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("MAIN2", "CHILD2"));
}
@Configuration
@EnableStateMachine
public static class Config2 extends StateMachineConfigurerAdapter<String, String> {
@@ -1791,6 +1802,25 @@ public class UmlStateMachineModelFactoryTests extends AbstractUmlTests {
}
}
@Configuration
@EnableStateMachine
public static class Config28 extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
model
.withModel()
.factory(modelFactory());
}
@Bean
public StateMachineModelFactory<String, String> modelFactory() {
Resource mainModel = new ClassPathResource("org/springframework/statemachine/uml/import-main/import-main.uml");
Resource subModel = new ClassPathResource("org/springframework/statemachine/uml/import-sub/import-sub.uml");
return new UmlStateMachineModelFactory(mainModel, new Resource[] { subModel });
}
}
public static class LatchAction implements Action<String, String> {
CountDownLatch latch = new CountDownLatch(1);
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2016 the original author or authors.
* Copyright 2016-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -97,4 +97,25 @@ public class DocsUmlSampleTests1 {
}
}
// end::snippetB[]
// tag::snippetC[]
@Configuration
@EnableStateMachine
public static class Config3 extends StateMachineConfigurerAdapter<String, String> {
@Override
public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
model
.withModel()
.factory(modelFactory());
}
@Bean
public StateMachineModelFactory<String, String> modelFactory() {
return new UmlStateMachineModelFactory(
"classpath:org/springframework/statemachine/uml/import-main/import-main.uml",
new String[] { "classpath:org/springframework/statemachine/uml/import-sub/import-sub.uml" });
}
}
// end::snippetC[]
}

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<architecture:ArchitectureDescription xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:architecture="http://www.eclipse.org/papyrus/infra/core/architecture" contextId="org.eclipse.papyrus.infra.services.edit.TypeContext"/>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<notation:Diagram xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.3/notation" xmlns:style="http://www.eclipse.org/papyrus/infra/gmfdiag/style" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_9FcQETIkEeuiF9TAc5z9jA" type="PapyrusUMLStateMachineDiagram" name="State Machine Diagram" measurementUnit="Pixel">
<children xmi:type="notation:Shape" xmi:id="_9FcQEjIkEeuiF9TAc5z9jA" type="StateMachine_Shape">
<children xmi:type="notation:DecorationNode" xmi:id="_9FcQEzIkEeuiF9TAc5z9jA" type="StateMachine_NameLabel">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_9FcQFDIkEeuiF9TAc5z9jA" width="700" height="20"/>
</children>
<children xmi:type="notation:BasicCompartment" xmi:id="_9FcQFTIkEeuiF9TAc5z9jA" type="StateMachine_RegionCompartment">
<children xmi:type="notation:Shape" xmi:id="_9FcQFjIkEeuiF9TAc5z9jA" type="Region_Shape">
<eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_9Fc3IDIkEeuiF9TAc5z9jA" source="RegionAnnotationKey">
<details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_9Fc3ITIkEeuiF9TAc5z9jA" key="RegionZoneKey" value=""/>
</eAnnotations>
<children xmi:type="notation:BasicCompartment" xmi:id="_9Fc3IjIkEeuiF9TAc5z9jA" type="Region_SubvertexCompartment">
<children xmi:type="notation:Shape" xmi:id="_T9844DIlEeuiF9TAc5z9jA" type="State_Shape">
<children xmi:type="notation:DecorationNode" xmi:id="_T99f8DIlEeuiF9TAc5z9jA" type="State_NameLabel"/>
<children xmi:type="notation:DecorationNode" xmi:id="_T99f8TIlEeuiF9TAc5z9jA" type="State_FloatingNameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_T99f8jIlEeuiF9TAc5z9jA" x="40"/>
</children>
<children xmi:type="notation:BasicCompartment" xmi:id="_T99f8zIlEeuiF9TAc5z9jA" type="State_RegionCompartment">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_T99f9DIlEeuiF9TAc5z9jA"/>
</children>
<element xmi:type="uml:State" href="import-main.uml#_T92yQDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_T9844TIlEeuiF9TAc5z9jA" x="109" y="50"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_VoRFoDIlEeuiF9TAc5z9jA" type="State_Shape">
<children xmi:type="notation:DecorationNode" xmi:id="_VoRFojIlEeuiF9TAc5z9jA" type="State_NameLabel"/>
<children xmi:type="notation:DecorationNode" xmi:id="_VoRFozIlEeuiF9TAc5z9jA" type="State_FloatingNameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_VoRFpDIlEeuiF9TAc5z9jA" x="40"/>
</children>
<children xmi:type="notation:BasicCompartment" xmi:id="_VoRFpTIlEeuiF9TAc5z9jA" type="State_RegionCompartment">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_VoRFpjIlEeuiF9TAc5z9jA"/>
</children>
<element xmi:type="uml:State" href="import-main.uml#_VoLmEDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_VoRFoTIlEeuiF9TAc5z9jA" x="249" y="50"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_XaYjgDIlEeuiF9TAc5z9jA" type="Pseudostate_InitialShape">
<children xmi:type="notation:DecorationNode" xmi:id="_XaYjgjIlEeuiF9TAc5z9jA" type="Pseudostate_InitialFloatingNameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_XaYjgzIlEeuiF9TAc5z9jA" x="25" y="3"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_XaYjhDIlEeuiF9TAc5z9jA" type="Pseudostate_InitialStereotypeLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_XaZKkDIlEeuiF9TAc5z9jA" x="25" y="-10"/>
</children>
<element xmi:type="uml:Pseudostate" href="import-main.uml#_XaQnsDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_XaYjgTIlEeuiF9TAc5z9jA" x="29" y="50"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_9Fc3IzIkEeuiF9TAc5z9jA"/>
</children>
<element xmi:type="uml:Region" href="import-main.uml#_9FcQEDIkEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_9Fc3JDIkEeuiF9TAc5z9jA" width="700" height="280"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_9Fc3JTIkEeuiF9TAc5z9jA" y="20" width="700" height="280"/>
</children>
<element xmi:type="uml:StateMachine" href="import-main.uml#_9FbpADIkEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_9Fc3JjIkEeuiF9TAc5z9jA" x="30" y="30" width="700" height="300"/>
</children>
<styles xmi:type="notation:StringValueStyle" xmi:id="_9Fc3JzIkEeuiF9TAc5z9jA" name="diagram_compatibility_version" stringValue="1.4.0"/>
<styles xmi:type="notation:DiagramStyle" xmi:id="_9Fc3KDIkEeuiF9TAc5z9jA"/>
<styles xmi:type="style:PapyrusDiagramStyle" xmi:id="_9Fc3KTIkEeuiF9TAc5z9jA" diagramKindId="org.eclipse.papyrus.uml.diagram.stateMachine">
<owner xmi:type="uml:Model" href="import-main.uml#_9FX-oDIkEeuiF9TAc5z9jA"/>
</styles>
<element xmi:type="uml:StateMachine" href="import-main.uml#_9FbpADIkEeuiF9TAc5z9jA"/>
<edges xmi:type="notation:Connector" xmi:id="_ZIx_ADIlEeuiF9TAc5z9jA" type="Transition_Edge" source="_XaYjgDIlEeuiF9TAc5z9jA" target="_T9844DIlEeuiF9TAc5z9jA">
<children xmi:type="notation:DecorationNode" xmi:id="_ZIx_AzIlEeuiF9TAc5z9jA" type="Transition_NameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_ZIx_BDIlEeuiF9TAc5z9jA" y="20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_ZIymEDIlEeuiF9TAc5z9jA" type="Transition_GuardLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_ZIymETIlEeuiF9TAc5z9jA" y="-20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_ZIymEjIlEeuiF9TAc5z9jA" type="Transition_StereotypeLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_ZIymEzIlEeuiF9TAc5z9jA" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_ZIx_ATIlEeuiF9TAc5z9jA"/>
<element xmi:type="uml:Transition" href="import-main.uml#_ZIlxwDIlEeuiF9TAc5z9jA"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_ZIx_AjIlEeuiF9TAc5z9jA" points="[78, 112, -643984, -643984]$[140, 120, -643984, -643984]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ZJGvIDIlEeuiF9TAc5z9jA" id="(0.9,0.6274193548387096)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ZJGvITIlEeuiF9TAc5z9jA" id="(0.0,0.45454545454545453)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_aPtx4DIlEeuiF9TAc5z9jA" type="Transition_Edge" source="_T9844DIlEeuiF9TAc5z9jA" target="_VoRFoDIlEeuiF9TAc5z9jA">
<children xmi:type="notation:DecorationNode" xmi:id="_aPtx4zIlEeuiF9TAc5z9jA" type="Transition_NameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_aPtx5DIlEeuiF9TAc5z9jA" y="20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_aPtx5TIlEeuiF9TAc5z9jA" type="Transition_GuardLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_aPtx5jIlEeuiF9TAc5z9jA" y="-20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_aPtx5zIlEeuiF9TAc5z9jA" type="Transition_StereotypeLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_aPtx6DIlEeuiF9TAc5z9jA" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_aPtx4TIlEeuiF9TAc5z9jA"/>
<element xmi:type="uml:Transition" href="import-main.uml#_aPiLsDIlEeuiF9TAc5z9jA"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_aPtx4jIlEeuiF9TAc5z9jA" points="[191, 120, -643984, -643984]$[280, 120, -643984, -643984]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_aQFlUDIlEeuiF9TAc5z9jA" id="(1.0,0.45454545454545453)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_aQFlUTIlEeuiF9TAc5z9jA" id="(0.0,0.45454545454545453)"/>
</edges>
</notation:Diagram>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_9FX-oDIkEeuiF9TAc5z9jA" name="import-main">
<packageImport xmi:type="uml:PackageImport" xmi:id="_9FxAMDIkEeuiF9TAc5z9jA">
<importedPackage xmi:type="uml:Model" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#_0"/>
</packageImport>
<packagedElement xmi:type="uml:StateMachine" xmi:id="_9FbpADIkEeuiF9TAc5z9jA" name="StateMachineMain">
<region xmi:type="uml:Region" xmi:id="_9FcQEDIkEeuiF9TAc5z9jA" name="Region1">
<transition xmi:type="uml:Transition" xmi:id="_ZIlxwDIlEeuiF9TAc5z9jA" source="_XaQnsDIlEeuiF9TAc5z9jA" target="_T92yQDIlEeuiF9TAc5z9jA"/>
<transition xmi:type="uml:Transition" xmi:id="_aPiLsDIlEeuiF9TAc5z9jA" source="_T92yQDIlEeuiF9TAc5z9jA" target="_VoLmEDIlEeuiF9TAc5z9jA"/>
<subvertex xmi:type="uml:State" xmi:id="_T92yQDIlEeuiF9TAc5z9jA" name="MAIN1"/>
<subvertex xmi:type="uml:State" xmi:id="_VoLmEDIlEeuiF9TAc5z9jA" name="MAIN2">
<submachine xmi:type="uml:StateMachine" href="../import-sub/import-sub.uml#_FylmYDIlEeuiF9TAc5z9jA"/>
</subvertex>
<subvertex xmi:type="uml:Pseudostate" xmi:id="_XaQnsDIlEeuiF9TAc5z9jA" name=""/>
</region>
</packagedElement>
</uml:Model>

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<architecture:ArchitectureDescription xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:architecture="http://www.eclipse.org/papyrus/infra/core/architecture" contextId="org.eclipse.papyrus.infra.services.edit.TypeContext"/>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<notation:Diagram xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.3/notation" xmlns:style="http://www.eclipse.org/papyrus/infra/gmfdiag/style" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_Fym0gDIlEeuiF9TAc5z9jA" type="PapyrusUMLStateMachineDiagram" name="State Machine Diagram" measurementUnit="Pixel">
<children xmi:type="notation:Shape" xmi:id="_Fym0gTIlEeuiF9TAc5z9jA" type="StateMachine_Shape">
<children xmi:type="notation:DecorationNode" xmi:id="_Fym0gjIlEeuiF9TAc5z9jA" type="StateMachine_NameLabel">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Fym0gzIlEeuiF9TAc5z9jA" width="700" height="20"/>
</children>
<children xmi:type="notation:BasicCompartment" xmi:id="_Fym0hDIlEeuiF9TAc5z9jA" type="StateMachine_RegionCompartment">
<children xmi:type="notation:Shape" xmi:id="_Fym0hTIlEeuiF9TAc5z9jA" type="Region_Shape">
<eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_Fym0hjIlEeuiF9TAc5z9jA" source="RegionAnnotationKey">
<details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_Fym0hzIlEeuiF9TAc5z9jA" key="RegionZoneKey" value=""/>
</eAnnotations>
<children xmi:type="notation:BasicCompartment" xmi:id="_Fym0iDIlEeuiF9TAc5z9jA" type="Region_SubvertexCompartment">
<children xmi:type="notation:Shape" xmi:id="_IqzwsDIlEeuiF9TAc5z9jA" type="State_Shape">
<children xmi:type="notation:DecorationNode" xmi:id="_Iq0XwDIlEeuiF9TAc5z9jA" type="State_NameLabel"/>
<children xmi:type="notation:DecorationNode" xmi:id="_Iq0XwTIlEeuiF9TAc5z9jA" type="State_FloatingNameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_Iq0XwjIlEeuiF9TAc5z9jA" x="40"/>
</children>
<children xmi:type="notation:BasicCompartment" xmi:id="_Iq0XwzIlEeuiF9TAc5z9jA" type="State_RegionCompartment">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Iq0XxDIlEeuiF9TAc5z9jA"/>
</children>
<element xmi:type="uml:State" href="import-sub.uml#_IqvfQDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_IqzwsTIlEeuiF9TAc5z9jA" x="109" y="50"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_LtpPkDIlEeuiF9TAc5z9jA" type="State_Shape">
<children xmi:type="notation:DecorationNode" xmi:id="_Ltp2oDIlEeuiF9TAc5z9jA" type="State_NameLabel"/>
<children xmi:type="notation:DecorationNode" xmi:id="_Ltp2oTIlEeuiF9TAc5z9jA" type="State_FloatingNameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_Ltp2ojIlEeuiF9TAc5z9jA" x="40"/>
</children>
<children xmi:type="notation:BasicCompartment" xmi:id="_Ltp2ozIlEeuiF9TAc5z9jA" type="State_RegionCompartment">
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Ltp2pDIlEeuiF9TAc5z9jA"/>
</children>
<element xmi:type="uml:State" href="import-sub.uml#_LtkXEDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_LtpPkTIlEeuiF9TAc5z9jA" x="249" y="50"/>
</children>
<children xmi:type="notation:Shape" xmi:id="_PK0hEDIlEeuiF9TAc5z9jA" type="Pseudostate_InitialShape">
<children xmi:type="notation:DecorationNode" xmi:id="_PK1IIDIlEeuiF9TAc5z9jA" type="Pseudostate_InitialFloatingNameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_PK1IITIlEeuiF9TAc5z9jA" x="25" y="3"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_PK1IIjIlEeuiF9TAc5z9jA" type="Pseudostate_InitialStereotypeLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_PK1IIzIlEeuiF9TAc5z9jA" x="25" y="-10"/>
</children>
<element xmi:type="uml:Pseudostate" href="import-sub.uml#_PKslQDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_PK0hETIlEeuiF9TAc5z9jA" x="38" y="57"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Fym0iTIlEeuiF9TAc5z9jA"/>
</children>
<element xmi:type="uml:Region" href="import-sub.uml#_FymNcDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Fym0ijIlEeuiF9TAc5z9jA" width="700" height="280"/>
</children>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Fym0izIlEeuiF9TAc5z9jA" y="20" width="700" height="280"/>
</children>
<element xmi:type="uml:StateMachine" href="import-sub.uml#_FylmYDIlEeuiF9TAc5z9jA"/>
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Fym0jDIlEeuiF9TAc5z9jA" x="30" y="30" width="700" height="300"/>
</children>
<styles xmi:type="notation:StringValueStyle" xmi:id="_Fym0jTIlEeuiF9TAc5z9jA" name="diagram_compatibility_version" stringValue="1.4.0"/>
<styles xmi:type="notation:DiagramStyle" xmi:id="_Fym0jjIlEeuiF9TAc5z9jA"/>
<styles xmi:type="style:PapyrusDiagramStyle" xmi:id="_Fym0jzIlEeuiF9TAc5z9jA" diagramKindId="org.eclipse.papyrus.uml.diagram.stateMachine">
<owner xmi:type="uml:Model" href="import-sub.uml#_FyeRoDIlEeuiF9TAc5z9jA"/>
</styles>
<element xmi:type="uml:StateMachine" href="import-sub.uml#_FylmYDIlEeuiF9TAc5z9jA"/>
<edges xmi:type="notation:Connector" xmi:id="_RRCmsDIlEeuiF9TAc5z9jA" type="Transition_Edge" source="_PK0hEDIlEeuiF9TAc5z9jA" target="_IqzwsDIlEeuiF9TAc5z9jA">
<children xmi:type="notation:DecorationNode" xmi:id="_RRCmszIlEeuiF9TAc5z9jA" type="Transition_NameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_RRCmtDIlEeuiF9TAc5z9jA" y="20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_RRCmtTIlEeuiF9TAc5z9jA" type="Transition_GuardLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_RRCmtjIlEeuiF9TAc5z9jA" y="-20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_RRCmtzIlEeuiF9TAc5z9jA" type="Transition_StereotypeLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_RRCmuDIlEeuiF9TAc5z9jA" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_RRCmsTIlEeuiF9TAc5z9jA"/>
<element xmi:type="uml:Transition" href="import-sub.uml#_RQ5cwDIlEeuiF9TAc5z9jA"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_RRCmsjIlEeuiF9TAc5z9jA" points="[87, 120, -643984, -643984]$[140, 120, -643984, -643984]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_RRUTgDIlEeuiF9TAc5z9jA" id="(0.9,0.65)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_RRUTgTIlEeuiF9TAc5z9jA" id="(0.0,0.45454545454545453)"/>
</edges>
<edges xmi:type="notation:Connector" xmi:id="_SN3bUDIlEeuiF9TAc5z9jA" type="Transition_Edge" source="_IqzwsDIlEeuiF9TAc5z9jA" target="_LtpPkDIlEeuiF9TAc5z9jA">
<children xmi:type="notation:DecorationNode" xmi:id="_SN4CYDIlEeuiF9TAc5z9jA" type="Transition_NameLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_SN4CYTIlEeuiF9TAc5z9jA" y="20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_SN4CYjIlEeuiF9TAc5z9jA" type="Transition_GuardLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_SN4CYzIlEeuiF9TAc5z9jA" y="-20"/>
</children>
<children xmi:type="notation:DecorationNode" xmi:id="_SN4CZDIlEeuiF9TAc5z9jA" type="Transition_StereotypeLabel">
<layoutConstraint xmi:type="notation:Location" xmi:id="_SN4CZTIlEeuiF9TAc5z9jA" y="60"/>
</children>
<styles xmi:type="notation:FontStyle" xmi:id="_SN3bUTIlEeuiF9TAc5z9jA"/>
<element xmi:type="uml:Transition" href="import-sub.uml#_SNtDQDIlEeuiF9TAc5z9jA"/>
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_SN3bUjIlEeuiF9TAc5z9jA" points="[197, 120, -643984, -643984]$[280, 120, -643984, -643984]"/>
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_SOLkYDIlEeuiF9TAc5z9jA" id="(1.0,0.45454545454545453)"/>
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_SOLkYTIlEeuiF9TAc5z9jA" id="(0.0,0.45454545454545453)"/>
</edges>
</notation:Diagram>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_FyeRoDIlEeuiF9TAc5z9jA" name="import-sub">
<packageImport xmi:type="uml:PackageImport" xmi:id="_FzAdIDIlEeuiF9TAc5z9jA">
<importedPackage xmi:type="uml:Model" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#_0"/>
</packageImport>
<packagedElement xmi:type="uml:StateMachine" xmi:id="_FylmYDIlEeuiF9TAc5z9jA" name="StateMachineSub">
<submachineState xmi:type="uml:State" href="../import-main/import-main.uml#_VoLmEDIlEeuiF9TAc5z9jA"/>
<region xmi:type="uml:Region" xmi:id="_FymNcDIlEeuiF9TAc5z9jA" name="Region1">
<transition xmi:type="uml:Transition" xmi:id="_RQ5cwDIlEeuiF9TAc5z9jA" source="_PKslQDIlEeuiF9TAc5z9jA" target="_IqvfQDIlEeuiF9TAc5z9jA"/>
<transition xmi:type="uml:Transition" xmi:id="_SNtDQDIlEeuiF9TAc5z9jA" source="_IqvfQDIlEeuiF9TAc5z9jA" target="_LtkXEDIlEeuiF9TAc5z9jA"/>
<subvertex xmi:type="uml:State" xmi:id="_IqvfQDIlEeuiF9TAc5z9jA" name="CHILD1"/>
<subvertex xmi:type="uml:State" xmi:id="_LtkXEDIlEeuiF9TAc5z9jA" name="CHILD2"/>
<subvertex xmi:type="uml:Pseudostate" xmi:id="_PKslQDIlEeuiF9TAc5z9jA" name=""/>
</region>
</packagedElement>
</uml:Model>