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: "\\[(Evolution)\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)", options: .caseInsensitive) static let note = try! NSRegularExpression( pattern: "\\[(Note)\\][\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)[\\s]*(.*)", 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 notes: [Note] let stages: [CGFloat] static let empty: ParsedMap = ParsedMap( vertices: [], edges: [], blockers: [], opportunities: [], notes: [], stages: defaultDimensions) } struct Vertex { let id: Int let label: String let position: CGPoint var shape: VertexShape = .circle } struct Note { let id: Int let position: CGPoint let text: String } 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(_ 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 notes: [Note] = [] 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 == Note.self { let note = object as! Note notes.append(note) } 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, notes: notes, stages: stages) } }