Skip to content

Commit

Permalink
[docs] Update MetaInfo documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
nsk90 committed Dec 12, 2024
1 parent 906c0eb commit 27fe906
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 9 deletions.
50 changes: 48 additions & 2 deletions docs/pages/export.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ title: Export
---

# Export

{: .no_toc }

## Page contents

{: .no_toc .text-delta }

- TOC
{:toc}
{:toc}

The library supports export into PlantUML and Mermaid diagram drawing systems. They both use PlantUML text format.
Mermaid supports fewer features then PlantUML itself.
Expand All @@ -27,6 +29,50 @@ but may cause runtime errors depending on what the lambda actually do. As it may
valid when export is running, also `event` argument will be faked by unsafe cast, so touching it
will cause `ClassCastException`
That is why `unsafeCallConditionalLambdas` flag should be considered as debug/development tool only.
The library provides additional `MetaInfo` objects that might be used along with `unsafeCallConditionalLambdas` flag
to provide complete output (with a help of a user).

* `IgnoreUnsafeCallConditionalLambdasMetaInfo` allows to ignore `unsafeCallConditionalLambdas` flag for some state or
transition
* `ExportMetaInfoBuilder` (is built by `buildExportMetaInfo` function) allows a user to manually specify hint
information (list of hints) for the library to print complete export output.
User is responsible to specify correct information.
There is `StateResolutionHint` which is useful to specify target states (so lambda execution is not needed)
and `EventAndArgumentResolutionHint` allowing to specify `Event` and argument, which will be used to execute a
conditional lambda. This allows to bypass limitations of default behaviour with fake event.

Here are some usage samples:
```kotlin
class ValueEvent(val value: Int) : Event

transitionConditionally<ValueEvent> {
direction = {
when (event.value) {
1 -> targetState(state1)
2 -> targetState(state2)
3 -> targetParallelStates(state1, state2)
4 -> stay()
else -> noTransition()
}
}
metaInfo = buildExportMetaInfo {
resolutionHints = setOf(
// the library does not need to call "direction" lambda, this hint provides the result (state1) directly
StateResolutionHint("when 1", state1),
// calls "direction" lambda during export with specified Event and optional argument (lambda will return state2)
EventAndArgumentResolutionHint("when 2", ValueEvent(2)),
// you can specify set of states that represents parallel target states
StateResolutionHint("when 3", setOf(state1, state2)),
// describes stay() behaviour without calling "direction" lambda
StateResolutionHint("when 4", this@createStateMachine),
// resolves to stay() by calling "direction" lambda
EventAndArgumentResolutionHint("when 4", ValueEvent(4)),
// useless, does not affect export output as it resolves to noTransition()
EventAndArgumentResolutionHint("else", ValueEvent(5)),
)
}
}
```

## PlantUML

Expand Down Expand Up @@ -64,7 +110,7 @@ See [Mermaid nested states export sample](https://github.com/KStateMachine/kstat

## Controlling export output

To beautify and enrich export output, you can use `UmlMetaInfo` for both `IState` and `Transition`. It can be built
To beautify and enrich export output, you can use `UmlMetaInfo` for both `IState` and `Transition`. It can be built
with `buildUmlMetaInfo()` function:

```kotlin
Expand Down
21 changes: 18 additions & 3 deletions docs/pages/meta_information.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,27 @@ title: Meta information

## `metaInfo` property

The library provides `metaInfo` property for `IState` and `Transition` types.
The library declares `metaInfo` property for `IState` and `Transition` types.
`MetaInfo` is a marker interface allowing to attach some static information to library primitives.
This mechanism is extendable and users may add their own `MetaInfo` sub interfaces/classes if necessary.
Currently, the only standard implementation is `UmlMetaInfo` which is useful for export feature.

One of standard implementations is `UmlMetaInfo` which is useful for export feature.
See [controlling export output](https://kstatemachine.github.io/kstatemachine/pages/export.html#controlling-export-output).
You can build it using `buildUmlMetaInfo()` function.

{: .note }
`MetaInfo` considered to be immutable data by design
`MetaInfo` considered to be immutable data by design

## MetaInfo composition

If you need to specify more than one `MetaInfo` subtypes for `IState` or `Transition` you have two options:
1) Use `CompositeMetaInfo` which is constructed by `buildCompositeMetaInfo` builder function.
This type allows to specify a set of `MetaInfo` objects.

Limitations:
* `CompositeMetaInfo` cannot nest into each other. Only one layer is supported.
* Don't try to specify `MetaInfo` of same type multiple times. This is wrong.
Certain `MetaInfo` subtype should be applied only once. Exception will be thrown otherwise.
2) Manually implement all required `MetaInfo` interfaces in a single object.

Both options are supported, choose any one you like more.
52 changes: 51 additions & 1 deletion kstatemachine/api/kstatemachine.api
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,57 @@ public final class ru/nsk/kstatemachine/event/WrappedEvent : ru/nsk/kstatemachin
public final fun getEvent ()Lru/nsk/kstatemachine/event/Event;
}

public abstract interface class ru/nsk/kstatemachine/metainfo/CompositeMetaInfo : ru/nsk/kstatemachine/metainfo/MetaInfo {
public abstract fun getMetaInfoSet ()Ljava/util/Set;
}

public abstract interface class ru/nsk/kstatemachine/metainfo/CompositeMetaInfoBuilder : ru/nsk/kstatemachine/metainfo/CompositeMetaInfo {
public abstract fun getMetaInfoSet ()Ljava/util/Set;
public abstract fun setMetaInfoSet (Ljava/util/Set;)V
}

public final class ru/nsk/kstatemachine/metainfo/EventAndArgumentResolutionHint : ru/nsk/kstatemachine/metainfo/ResolutionHint {
public fun <init> (Ljava/lang/String;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;)V
public synthetic fun <init> (Ljava/lang/String;Lru/nsk/kstatemachine/event/Event;Ljava/lang/Object;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getArgument ()Ljava/lang/Object;
public final fun getDescription ()Ljava/lang/String;
public final fun getEvent ()Lru/nsk/kstatemachine/event/Event;
public final fun getEventAndArgument ()Lru/nsk/kstatemachine/transition/EventAndArgument;
}

public abstract interface class ru/nsk/kstatemachine/metainfo/ExportMetaInfo : ru/nsk/kstatemachine/metainfo/MetaInfo {
public abstract fun getResolutionHints ()Ljava/util/Set;
}

public abstract interface class ru/nsk/kstatemachine/metainfo/ExportMetaInfoBuilder : ru/nsk/kstatemachine/metainfo/ExportMetaInfo {
public abstract fun getResolutionHints ()Ljava/util/Set;
public abstract fun setResolutionHints (Ljava/util/Set;)V
}

public final class ru/nsk/kstatemachine/metainfo/ExportMetaInfoKt {
public static final fun buildExportMetaInfo (Lkotlin/jvm/functions/Function1;)Lru/nsk/kstatemachine/metainfo/ExportMetaInfo;
}

public final class ru/nsk/kstatemachine/metainfo/IgnoreUnsafeCallConditionalLambdasMetaInfo : ru/nsk/kstatemachine/metainfo/MetaInfo {
public static final field INSTANCE Lru/nsk/kstatemachine/metainfo/IgnoreUnsafeCallConditionalLambdasMetaInfo;
}

public abstract interface class ru/nsk/kstatemachine/metainfo/MetaInfo {
}

public final class ru/nsk/kstatemachine/metainfo/MetaInfoKt {
public static final fun buildUmlMetaInfo (Lkotlin/jvm/functions/Function1;)Lru/nsk/kstatemachine/metainfo/UmlMetaInfo;
public static final fun buildCompositeMetaInfo (Lkotlin/jvm/functions/Function1;)Lru/nsk/kstatemachine/metainfo/CompositeMetaInfo;
public static final fun buildCompositeMetaInfo (Lru/nsk/kstatemachine/metainfo/MetaInfo;Lru/nsk/kstatemachine/metainfo/MetaInfo;[Lru/nsk/kstatemachine/metainfo/MetaInfo;)Lru/nsk/kstatemachine/metainfo/CompositeMetaInfo;
}

public abstract interface class ru/nsk/kstatemachine/metainfo/ResolutionHint {
}

public final class ru/nsk/kstatemachine/metainfo/StateResolutionHint : ru/nsk/kstatemachine/metainfo/ResolutionHint {
public fun <init> (Ljava/lang/String;Ljava/util/Set;)V
public fun <init> (Ljava/lang/String;Lru/nsk/kstatemachine/state/IState;)V
public final fun getDescription ()Ljava/lang/String;
public final fun getTargetStates ()Ljava/util/Set;
}

public abstract interface class ru/nsk/kstatemachine/metainfo/UmlMetaInfo : ru/nsk/kstatemachine/metainfo/MetaInfo {
Expand All @@ -113,6 +159,10 @@ public abstract interface class ru/nsk/kstatemachine/metainfo/UmlMetaInfoBuilder
public abstract fun setUmlStateDescriptions (Ljava/util/List;)V
}

public final class ru/nsk/kstatemachine/metainfo/UmlMetaInfoKt {
public static final fun buildUmlMetaInfo (Lkotlin/jvm/functions/Function1;)Lru/nsk/kstatemachine/metainfo/UmlMetaInfo;
}

public final class ru/nsk/kstatemachine/persistence/EmptyValidator : ru/nsk/kstatemachine/persistence/RestorationResultValidator {
public static final field INSTANCE Lru/nsk/kstatemachine/persistence/EmptyValidator;
public fun validate (Lru/nsk/kstatemachine/persistence/RestorationResult;Lru/nsk/kstatemachine/persistence/RecordedEvents;Lru/nsk/kstatemachine/statemachine/StateMachine;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ package ru.nsk.kstatemachine.metainfo

import ru.nsk.kstatemachine.event.Event
import ru.nsk.kstatemachine.state.IState
import ru.nsk.kstatemachine.state.InternalState
import ru.nsk.kstatemachine.state.RedirectPseudoState
import ru.nsk.kstatemachine.transition.EventAndArgument

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface MetaInfo
* Allows to specify multiple [MetaInfo] objects.
* It might be simpler than constructing single object implementing multiple [MetaInfo] derived interfaces.
* Nesting [CompositeMetaInfo] into each other is not supported.
* Only one instance of certain [MetaInfo] subtype should be specified.
*/
interface CompositeMetaInfo : MetaInfo {
/**
Expand All @@ -29,7 +30,10 @@ interface CompositeMetaInfo : MetaInfo {
val metaInfoSet: Set<MetaInfo>
}

internal inline fun <reified M : MetaInfo> MetaInfo?.findMetaInfo(): M? {
/**
* Helper method for getting [MetaInfo] of specified type
*/
inline fun <reified M : MetaInfo> MetaInfo?.findMetaInfo(): M? {
return when (this) {
is M -> this
is CompositeMetaInfo -> metaInfoSet.singleOrNull { it is M } as? M
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ internal class ExportPlantUmlVisitor(
private fun transitionLabel(text: String?) = " : $text".takeIf { text?.isNotBlank() == true } ?: ""

private fun IState.printStateDescriptions() {
val descriptions = (metaInfo as? UmlMetaInfo)?.umlStateDescriptions.orEmpty()
val descriptions = metaInfo.findMetaInfo<UmlMetaInfo>()?.umlStateDescriptions.orEmpty()
descriptions.forEach { line("${graphName()} : $it") }
}

Expand Down

0 comments on commit 27fe906

Please sign in to comment.