X-Git-Url: https://git.r.bdr.sh/rbdr/map/blobdiff_plain/5e8ff4850c4827125fe12788dd5b153c4f636f48..144167621e845b05df55a9f2f00c4bc270d42b55:/Map/Extensions/Map%20parse.swift/rbdr/map/blobdiff_plain/5e8ff4850c4827125fe12788dd5b153c4f636f48..144167621e845b05df55a9f2f00c4bc270d42b55:/Map/Extensions/Map+parse.swift?ds=inline diff --git a/Map/Extensions/Map+parse.swift b/Map/Extensions/Map+parse.swift index d82ba99..904f492 100644 --- a/Map/Extensions/Map+parse.swift +++ b/Map/Extensions/Map+parse.swift @@ -1,215 +1,28 @@ -import CoreGraphics -import Foundation - -let vertexPattern = - "([^\\(]+?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)" -let edgePattern = "(.+?)[\\s]*-([->])[\\s]*(.+)" -let blockerPattern = "\\[Blocker\\][\\s]*(.+)" -let opportunityPattern = "\\[Opportunity\\][\\s]*(.+)[\\s]+([-+])[\\s]*([0-9]+.?[0-9]*)" -let stagePattern = "\\[(I{1,3})\\][\\s]*([0-9]+.?[0-9]*)" - -struct ParsedMap { - let vertices: [Vertex] - let edges: [MapEdge] - let blockers: [Blocker] - let opportunities: [Opportunity] - let stages: [CGFloat] -} - -struct Vertex { - let id: Int - let label: String - let position: CGPoint -} - -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 -} - -let defaultDimensions: [CGFloat] = [ - 25.0, - 50.0, - 75.0, -] - -// Extracts the vertices from the text - -func parseVertices(_ text: String) -> [String: CGPoint] { - - var result: [String: CGPoint] = [:] - let regex = try! NSRegularExpression(pattern: vertexPattern, options: .caseInsensitive) - - let lines = text.split(whereSeparator: \.isNewline) - - for line in lines { - let range = NSRange(location: 0, length: line.utf16.count) - let matches = regex.matches(in: String(line), options: [], range: range) - - if matches.count > 0 && matches[0].numberOfRanges == 4 { - - let match = matches[0] - let key = String(line[Range(match.range(at: 1), in: line)!]) - let xString = String(line[Range(match.range(at: 2), in: line)!]) - let yString = String(line[Range(match.range(at: 3), in: line)!]) - let x = CGFloat(truncating: NumberFormatter().number(from: xString) ?? 0.0) - let y = CGFloat(truncating: NumberFormatter().number(from: yString) ?? 0.0) - let point = CGPoint(x: x, y: y) - - result[key] = point - } - } - - return result -} - -func parseEdges(_ text: String, vertices: [String: CGPoint]) -> [MapEdge] { - - var result: [MapEdge] = [] - let regex = try! NSRegularExpression(pattern: edgePattern, options: .caseInsensitive) - - let lines = text.split(whereSeparator: \.isNewline) - - for (index, line) in lines.enumerated() { - let range = NSRange(location: 0, length: line.utf16.count) - let matches = regex.matches(in: String(line), options: [], range: range) - - if matches.count > 0 && matches[0].numberOfRanges == 4 { - - let match = matches[0] - let arrowhead = String(line[Range(match.range(at: 2), in: line)!]) == ">" - let vertexA = String(line[Range(match.range(at: 1), in: line)!]) - let vertexB = String(line[Range(match.range(at: 3), in: line)!]) - - if let origin = vertices[vertexA] { - if let destination = vertices[vertexB] { - result.append( - MapEdge(id: index, origin: origin, destination: destination, arrowhead: arrowhead)) +extension Map { + static func parse(content: String) -> ParsedMap { + + let parsers = [ + AnyMapParserStrategy(VertexParserStrategy()), + AnyMapParserStrategy(EdgeParserStrategy()), + AnyMapParserStrategy(BlockerParserStrategy()), + AnyMapParserStrategy(OpportunityParserStrategy()), + AnyMapParserStrategy(StageParserStrategy()), + ] + let builder = MapBuilder() + + let lines = content.split(whereSeparator: \.isNewline) + + for (index, line) in lines.enumerated() { + for parser in parsers { + if parser.canHandle(line: String(line)) { + let (type, object) = parser.handle( + index: index, line: String(line), vertices: builder.vertices) + builder.addObjectToMap(type: type, object: object) + break } } } - } - - return result -} - -func parseOpportunities(_ text: String, vertices: [String: CGPoint]) -> [Opportunity] { - - var result: [Opportunity] = [] - let regex = try! NSRegularExpression(pattern: opportunityPattern, options: .caseInsensitive) - - let lines = text.split(whereSeparator: \.isNewline) - - for (index, line) in lines.enumerated() { - let range = NSRange(location: 0, length: line.utf16.count) - let matches = regex.matches(in: String(line), options: [], range: range) - - if matches.count > 0 && matches[0].numberOfRanges == 4 { - - let match = matches[0] - let multiplier = CGFloat( - String(line[Range(match.range(at: 2), in: line)!]) == "-" ? -1.0 : 1.0) - let vertex = String(line[Range(match.range(at: 1), in: line)!]) - let opportunityString = String(line[Range(match.range(at: 3), in: line)!]) - let opportunity = CGFloat( - truncating: NumberFormatter().number(from: opportunityString) ?? 0.0) - - if let origin = vertices[vertex] { - let destination = CGPoint(x: origin.x + opportunity * multiplier, y: origin.y) - result.append(Opportunity(id: index, origin: origin, destination: destination)) - } - } - } - - return result -} - -func parseBlockers(_ text: String, vertices: [String: CGPoint]) -> [Blocker] { - - var result: [Blocker] = [] - let regex = try! NSRegularExpression(pattern: blockerPattern, options: .caseInsensitive) - - let lines = text.split(whereSeparator: \.isNewline) - - for (index, line) in lines.enumerated() { - let range = NSRange(location: 0, length: line.utf16.count) - let matches = regex.matches(in: String(line), options: [], range: range) - - if matches.count > 0 && matches[0].numberOfRanges == 2 { - - let match = matches[0] - let vertexA = String(line[Range(match.range(at: 1), in: line)!]) - - if let position = vertices[vertexA] { - result.append(Blocker(id: index, position: position)) - } - } - } - - return result -} - -func parseStages(_ text: String) -> [CGFloat] { - - var result = defaultDimensions - let regex = try! NSRegularExpression(pattern: stagePattern, options: .caseInsensitive) - - let lines = text.split(whereSeparator: \.isNewline) - - for line in lines { - let range = NSRange(location: 0, length: line.utf16.count) - let matches = regex.matches(in: String(line), options: [], range: range) - - if matches.count > 0 && matches[0].numberOfRanges == 3 { - - let match = matches[0] - let stage = String(line[Range(match.range(at: 1), in: line)!]) - let dimensionsString = String(line[Range(match.range(at: 2), in: line)!]) - let dimensions = CGFloat(truncating: NumberFormatter().number(from: dimensionsString) ?? 0.0) - - result[stage.count - 1] = dimensions - } - } - - return result -} - -// Converts vetex dictionary to array - -func mapVertices(_ vertices: [String: CGPoint]) -> [Vertex] { - var i = 0 - return vertices.map { label, position in - i += 1 - return Vertex(id: i, label: label, position: position) - } -} - -extension Map { - func parse() -> ParsedMap { - - let text = self.content ?? "" - let vertices = parseVertices(text) - let mappedVertices = mapVertices(vertices) - let edges = parseEdges(text, vertices: vertices) - let blockers = parseBlockers(text, vertices: vertices) - let opportunities = parseOpportunities(text, vertices: vertices) - let stages = parseStages(text) - return ParsedMap( - vertices: mappedVertices, edges: edges, blockers: blockers, opportunities: opportunities, - stages: stages) + return builder.build() } }