1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
import CoreGraphics
import Foundation
// MARK: - Types
struct MapParsingPatterns {
static let vertex = try! NSRegularExpression(
pattern:
"([^\\(]+?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(?:\\[(.*?)\\])?",
options: .caseInsensitive)
static let edge = try! NSRegularExpression(
pattern: "(.+?)[\\s]*-([->])[\\s]*(.+)", options: .caseInsensitive)
static let blocker = try! NSRegularExpression(
pattern: "\\[(Blocker)\\][\\s]*(.+)", options: .caseInsensitive)
static let opportunity = try! NSRegularExpression(
pattern: "\\[(Opportunity)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)",
options: .caseInsensitive)
static let stage = try! NSRegularExpression(
pattern: "\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)", options: .caseInsensitive)
}
struct ParsedMap {
let vertices: [Vertex]
let edges: [MapEdge]
let blockers: [Blocker]
let opportunities: [Opportunity]
let stages: [CGFloat]
static let empty: ParsedMap = ParsedMap(
vertices: [], edges: [], blockers: [], opportunities: [], stages: defaultDimensions)
}
struct Vertex {
let id: Int
let label: String
let position: CGPoint
var shape: VertexShape = .circle
}
enum VertexShape: String {
case circle
case square
case triangle
case x
}
struct MapEdge {
let id: Int
let origin: CGPoint
let destination: CGPoint
let arrowhead: Bool
}
struct Blocker {
let id: Int
let position: CGPoint
}
struct Opportunity {
let id: Int
let origin: CGPoint
let destination: CGPoint
}
struct StageDimensions {
let index: Int
let dimensions: CGFloat
}
private let defaultDimensions: [CGFloat] = [
25.0,
50.0,
75.0,
]
// MARK: - MapParserStrategy protocol
protocol MapParserStrategy {
func canHandle(line: String) -> Bool
func handle(index: Int, line: String, vertices: [String: Vertex]) -> (Any.Type, Any)
}
struct AnyMapParserStrategy: MapParserStrategy {
private let base: MapParserStrategy
init<T: MapParserStrategy>(_ base: T) {
self.base = base
}
func canHandle(line: String) -> Bool {
return base.canHandle(line: line)
}
func handle(index: Int, line: String, vertices: [String: Vertex]) -> (Any.Type, Any) {
return base.handle(index: index, line: line, vertices: vertices)
}
}
// MARK: - Map Builder
class MapBuilder {
var vertices: [String: Vertex] = [:]
private var edges: [MapEdge] = []
private var blockers: [Blocker] = []
private var opportunities: [Opportunity] = []
private var stages: [CGFloat] = defaultDimensions
func addObjectToMap(type: Any.Type, object: Any) {
if type == Vertex.self {
let vertex = object as! Vertex
vertices[vertex.label] = vertex
}
if type == MapEdge.self {
let edge = object as! MapEdge
edges.append(edge)
}
if type == Blocker.self {
let blocker = object as! Blocker
blockers.append(blocker)
}
if type == Opportunity.self {
let opportunity = object as! Opportunity
opportunities.append(opportunity)
}
if type == StageDimensions.self {
let stageDimensions = object as! StageDimensions
stages[stageDimensions.index] = stageDimensions.dimensions
}
}
func build() -> ParsedMap {
let mappedVertices = vertices.map { label, vertex in return vertex }
return ParsedMap(
vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities,
stages: stages)
}
}
|