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