0001 // 0002 // Transporter.swift 0003 // Transporter 0004 // 0005 // Created by Denys Telezhkin on 14.10.14. 0006 // Copyright (c) 2014 Denys Telezhkin. All rights reserved. 0007 // 0008 // Permission is hereby granted, free of charge, to any person obtaining a copy 0009 // of this software and associated documentation files (the "Software"), to deal 0010 // in the Software without restriction, including without limitation the rights 0011 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 0012 // copies of the Software, and to permit persons to whom the Software is 0013 // furnished to do so, subject to the following conditions: 0014 // 0015 // The above copyright notice and this permission notice shall be included in 0016 // all copies or substantial portions of the Software. 0017 // 0018 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 0019 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 0020 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 0021 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 0022 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 0023 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 0024 // THE SOFTWARE. 0025 0026 /** 0027 Enum with possible transition errors. These are meant to be used inside fireEvent method on StateMachine. They will be included as status codes inside NSError, that Transition.Error enum returns. 0028 */ 0029 public enum TransitionError: ErrorType { 0030 /** 0031 When event's shouldFireEvent closure returns false, `TransitionDeclined` error will be returned as a status code inside NSError object. 0032 */ 0033 case TransitionDeclined
Event.swift:52 case Error(TransitionError)0034 0035 /** 0036 `UnknownEvent` means there's no such event on `StateMachine`. 0037 */ 0038 case UnknownEvent
StateMachine.swift:297 return .Error(.TransitionDeclined)0039 0040 /** 0041 `WrongSourceState` means, that source states for this fired event do not include state, in which StateMachine is currently in. 0042 */ 0043 case WrongSourceState
StateMachine.swift:233 return .Error(.UnknownEvent)StateMachine.swift:312 return .Error(.UnknownEvent)0044 0045 /// `WrongType` means, that Event states don't match `StateMachine` or `State` type T. 0046 case WrongEventType 0047 } 0048 0049 /// This enum contains events, that can happen when adding event to state machine. 0050 public enum EventError
StateMachine.swift:238 return .Error(.WrongSourceState): ErrorType { 0051 0052 /// `NoSourceValue` means, that when adding `Event` to `StateMachine` one of source state values of event was not present on state machine 0053 case NoSourceValue
StateMachine.swift:149 throw EventError.NoSourceValueStateMachine.swift:156 throw EventError.NoSourceValueStateMachine.swift:160 throw EventError.NoDestinationValue0054 0055 /// `NoDestinationValue` means, that when adding `Event` to `StateMachine` destination state value of event was not present on state machine 0056 case NoDestinationValue
StateMachine.swift:149 throw EventError.NoSourceValueStateMachine.swift:156 throw EventError.NoSourceValue0057 } 0058 0059 /// `StateMachine` is a state machine, obviously =). 0060 public class StateMachine
StateMachine.swift:160 throw EventError.NoDestinationValue<T
StateMachine.swift:270 private extension StateMachine {:Hashable> { 0061 0062 /// Initial state of state machine. 0063 var initialState
StateMachine.swift:63 var initialState: State<T>StateMachine.swift:66 public private(set) var currentState : State<T>StateMachine.swift:69 private lazy var availableStates : [State<T>] = []StateMachine.swift:72 private lazy var events : [Event<T>] = []StateMachine.swift:76 required public init(initialState: State<T>)StateMachine.swift:85 convenience public init(initialStateValue: T)StateMachine.swift:94 convenience public init(initialState: State<T>, states: [State<T>])StateMachine.swift:94 convenience public init(initialState: State<T>, states: [State<T>])StateMachine.swift:103 public func activateState(stateValue: T) {StateMachine.swift:121 public func isStateAvailable(stateValue: T) -> Bool {StateMachine.swift:133 public func addState(state: State<T>) {StateMachine.swift:139 public func addStates(states: [State<T>]) {StateMachine.swift:146 public func addEvent(event: Event<T>) throws {StateMachine.swift:168 public func addEvents(events: [Event<T>]) {StateMachine.swift:190 public func fireEvent(event: Event<T>) -> Transition<T> {StateMachine.swift:190 public func fireEvent(event: Event<T>) -> Transition<T> {StateMachine.swift:197 public func fireEvent(eventName: String) -> Transition<T> {StateMachine.swift:204 public func canFireEvent(event: Event<T>) -> Bool {StateMachine.swift:231 public func possibleTransitionForEvent(event: Event<T>) -> Transition<T> {StateMachine.swift:231 public func possibleTransitionForEvent(event: Event<T>) -> Transition<T> {StateMachine.swift:247 public func stateWithValue(value: T) -> State<T>? {StateMachine.swift:247 public func stateWithValue(value: T) -> State<T>? {StateMachine.swift:256 public func eventWithName(name: String) -> Event<T>? {StateMachine.swift:265 public func isInState(stateValue: T) -> Bool {: State<T> 0064 0065 /// Current state of state machine 0066 public private(set) var currentState
StateMachine.swift:78 self.initialState = initialState: State<T> 0067 0068 /// Available states in state machine 0069 private lazy var availableStates
StateMachine.swift:79 self.currentState = initialStateStateMachine.swift:106 let oldState = currentStateStateMachine.swift:112 currentState = newStateStateMachine.swift:115 newState.didEnterState?(enteringState: currentState)StateMachine.swift:235 if event.sourceValues.contains(currentState.value) {StateMachine.swift:236 return Transition.Success(sourceState: currentState, destinationState: State(event.destinationValue))StateMachine.swift:266 return stateValue == currentState.valueStateMachine.swift:301 let sourceState = self.currentState: [State<T>] = [] 0070 0071 /// Available events in state machine 0072 private lazy var events
StateMachine.swift:80 availableStates.append(initialState)StateMachine.swift:97 self.availableStates.appendContentsOf(states)StateMachine.swift:122 let states = availableStates.filter { (element) -> Bool inStateMachine.swift:134 availableStates.append(state)StateMachine.swift:140 availableStates.appendContentsOf(states)StateMachine.swift:248 return availableStates.filter { (element) -> Bool in: [Event<T>] = [] 0073 0074 /// Create `StateMachine` with initialState 0075 /// - Parameter initialState: initial state of state machine 0076 required public init
StateMachine.swift:163 self.events.append(event)StateMachine.swift:232 if !events.contains(event) {StateMachine.swift:257 return events.filter { (element) -> Bool in(initialState: State<T>) 0077 { 0078 self.initialState = initialState 0079 self.currentState = initialState 0080 availableStates.append(initialState) 0081 } 0082 0083 /// Create `StateMachine` with initialState value 0084 /// - Parameter initialStateValue: initial state value. 0085 convenience public init(initialStateValue: T) 0086 { 0087 self.init(initialState:State(initialStateValue)) 0088 } 0089 0090 /// Create `StateMachine` with initialState and array of states 0091 /// - Parameter initialState: initial state. 0092 /// - Parameter states: Array of states of state machine 0093 /// - Discussion: Initial state can be omitted from list of states, because it will already be added from first parameter. 0094 convenience public init(initialState: State<T>, states: [State<T>]) 0095 { 0096 self.init(initialState: initialState) 0097 self.availableStates.appendContentsOf(states) 0098 } 0099 0100 /// Activate state, if it's present in `StateMachine`. This method is not tied to events, present in StateMachine. 0101 /// - Parameter stateValue: value of state to switch to. 0102 /// - Note: This method basically breaks conditional finite-state machine rules, because it does not check any conditions between states. However, it allows more simple state-machine usage, if you don't need complicated events system. 0103 public func activateState
StateMachine.swift:87 self.init(initialState:State(initialStateValue))StateMachine.swift:96 self.init(initialState: initialState)(stateValue: T) { 0104 if (isStateAvailable(stateValue)) 0105 { 0106 let oldState = currentState 0107 let newState = stateWithValue(stateValue)! 0108 0109 newState.willEnterState?(enteringState: newState) 0110 oldState.willExitState?(exitingState: oldState) 0111 0112 currentState = newState 0113 0114 oldState.didExitState?(exitingState: oldState) 0115 newState.didEnterState?(enteringState: currentState) 0116 } 0117 } 0118 0119 /// If state is present in available states in `StateMachine`, this method will return true. This method does not check events on `StateMachine`. 0120 /// - Parameter stateValue: value of state to check availability for. 0121 public func isStateAvailable
StateMachine.swift:292 activateState(event.destinationValue)StateMachine.swift:303 activateState(event.destinationValue)(stateValue: T) -> Bool { 0122 let states = availableStates.filter { (element) -> Bool in 0123 return element.value == stateValue 0124 } 0125 if !states.isEmpty { 0126 return true 0127 } 0128 return false 0129 } 0130 0131 /// Add state to array of available states 0132 /// - Parameter state: state to add 0133 public func addState(state: State<T>) { 0134 availableStates.append(state) 0135 } 0136 0137 /// Add array of states 0138 /// - Parameter states: states array. 0139 public func addStates(states: [State<T>]) { 0140 availableStates.appendContentsOf(states) 0141 } 0142 0143 /// Add event to `StateMachine`. This method checks, whether source states and destination state of event are present in `StateMachine`. If not - event will not be added, and this method will throw. 0144 /// - Parameter event: event to add. 0145 /// - Throws: `EventError` if event cannot be added. 0146 public func addEvent
StateMachine.swift:104 if (isStateAvailable(stateValue))(event: Event<T>) throws { 0147 if event.sourceValues.isEmpty 0148 { 0149 throw EventError.NoSourceValue 0150 } 0151 0152 for state in event.sourceValues 0153 { 0154 if (self.stateWithValue(state) == nil) 0155 { 0156 throw EventError.NoSourceValue 0157 } 0158 } 0159 if (self.stateWithValue(event.destinationValue) == nil) { 0160 throw EventError.NoDestinationValue 0161 } 0162 0163 self.events.append(event) 0164 } 0165 0166 /// Add events to `StateMachine`. This method checks, whether source states and destination state of event are present in `StateMachine`. If not - event will not be added. 0167 /// - Parameter events: events to add to `StateMachine`. 0168 public func addEvents(events: [Event<T>]) { 0169 for event in events 0170 { 0171 guard let _ = try? self.addEvent(event) else { 0172 print("failed adding event with name: %@",event.name) 0173 continue 0174 } 0175 } 0176 } 0177 0178 /** 0179 Fires event. Several checks are made along the way: 0180 0181 1. Event is present in StateMachine. If not - error includes UnknownEvent transition error 0182 2. Event source states include current StateMachine state. If not - error includes WrongSourceState transition error 0183 3. Event shouldFireEvent closure is fired. If it returned false - error includes TransitionDeclined transition error. 0184 0185 If all conditions passed, event is fired, all closures are fired, and StateMachine changes it's state to event.destinationState. 0186 0187 - parameter event: event to fire 0188 - returns: `Transition` object. 0189 */ 0190 public func fireEvent(event: Event<T>) -> Transition<T> { 0191 return _fireEventNamed(event.name) 0192 } 0193 0194 /// This method is a convenience shortcut to fireEvent(event: Event<StateType>) method above. 0195 /// - Parameter eventName: name of event to fire. 0196 /// - Returns: `Transition` object. 0197 public func fireEvent(eventName: String) -> Transition<T> { 0198 return _fireEventNamed(eventName) 0199 } 0200 0201 /// Returns, whether event can be fired (event is present on state machine, current state is in list of available states) 0202 /// - Parameter event: Event 0203 /// - Returns: whether event can be fired 0204 public func canFireEvent
StateMachine.swift:171 guard let _ = try? self.addEvent(event) else {(event: Event<T>) -> Bool { 0205 let possibleTransition = possibleTransitionForEvent(event) 0206 if case .Error(_) = possibleTransition { 0207 return false 0208 } 0209 return true 0210 } 0211 0212 /** 0213 This method checks all conditions mentioned in fireEvent method, and returns Bool. 0214 0215 - Parameter eventName: name of event that could be fired 0216 - Returns: true, if event can be fired, false if don't 0217 */ 0218 public func canFireEvent(eventName: String) -> Bool { 0219 if let event = eventWithName(eventName) 0220 { 0221 return canFireEvent(event) 0222 } 0223 return false 0224 } 0225 0226 /** 0227 Retrieve possible transition for event. This transition is a representation of transition, that will happen, if event will be fired. `StateMachine` does not change it's state when this method is called. 0228 - Parameter event: event that could be fired. 0229 - Returns: `Transition` object. 0230 */ 0231 public func possibleTransitionForEvent
StateMachine.swift:221 return canFireEvent(event)(event: Event<T>) -> Transition<T> { 0232 if !events.contains(event) { 0233 return .Error(.UnknownEvent) 0234 } 0235 if event.sourceValues.contains(currentState.value) { 0236 return Transition.Success(sourceState: currentState, destinationState: State(event.destinationValue)) 0237 } 0238 return .Error(.WrongSourceState) 0239 } 0240 0241 /** 0242 Retrieve state with specific value. 0243 0244 - Parameter value: value of state to search for 0245 - Returns: state, if found. 0246 */ 0247 public func stateWithValue
StateMachine.swift:205 let possibleTransition = possibleTransitionForEvent(event)StateMachine.swift:286 let possibleTransition = possibleTransitionForEvent(event)(value: T) -> State<T>? { 0248 return availableStates.filter { (element) -> Bool in 0249 return element.value == value 0250 }.first 0251 } 0252 0253 /// Retrieve event with specific name 0254 /// - Parameter name: Name of the event 0255 /// - Returns: event, if found. 0256 public func eventWithName
StateMachine.swift:107 let newState = stateWithValue(stateValue)!StateMachine.swift:154 if (self.stateWithValue(state) == nil)StateMachine.swift:159 if (self.stateWithValue(event.destinationValue) == nil) {(name: String) -> Event<T>? { 0257 return events.filter { (element) -> Bool in 0258 return element.name == name 0259 }.first 0260 } 0261 0262 /// Check, whether state machine is in concrete state 0263 /// - Parameter stateValue: value of state to check for 0264 /// - Returns: whether state machine is in this state. 0265 public func isInState(stateValue: T) -> Bool { 0266 return stateValue == currentState.value 0267 } 0268 } 0269 0270 private extension StateMachine { 0271 0272 /** 0273 Fire event with name. Several checks are made along the way. 0274 0275 1. If no event found with this name, .UnknownEvent error is returned 0276 2. possibleTransitionForEvent(_:) method is calledm of it errors, it is immediately returned 0277 3. shouldFireEvent block is run, if it fails, .TransitionDeclined error is returned. 0278 4. Event is fired, triggering willFireEvent and didFireEvent closures. All state enter and exit closures are also run. 0279 0280 - Parameter eventName: name of event to fire 0281 - Returns: `Transition` object. 0282 */ 0283 /// 0284 func _fireEventNamed
StateMachine.swift:219 if let event = eventWithName(eventName)StateMachine.swift:285 if let event = eventWithName(eventName) {(eventName: String) -> Transition<T> { 0285 if let event = eventWithName(eventName) { 0286 let possibleTransition = possibleTransitionForEvent(event) 0287 switch possibleTransition { 0288 case .Success(let sourceState, let destinationState): 0289 if let shouldBlock = event.shouldFireEvent { 0290 if shouldBlock(event: event) { 0291 event.willFireEvent?(event: event) 0292 activateState(event.destinationValue) 0293 event.didFireEvent?(event: event) 0294 return .Success(sourceState: sourceState, destinationState: destinationState) 0295 } 0296 else { 0297 return .Error(.TransitionDeclined) 0298 } 0299 } 0300 else { 0301 let sourceState = self.currentState 0302 event.willFireEvent?(event: event) 0303 activateState(event.destinationValue) 0304 event.didFireEvent?(event: event) 0305 return .Success(sourceState: sourceState, destinationState: destinationState) 0306 } 0307 default : 0308 return possibleTransition 0309 } 0310 } 0311 else { 0312 return .Error(.UnknownEvent) 0313 } 0314 } 0315 } 0316
StateMachine.swift:191 return _fireEventNamed(event.name)StateMachine.swift:198 return _fireEventNamed(eventName)