PathParser { public static void parse(String pathString, Path p) { int n = pathString.length(); ParserHelper ph = new ParserHelper(pathString, 0); ph.skipWhitespace(); float lastX = 0; float lastY = 0; float lastX1 = 0; float lastY1 = 0; float subPathStartX = 0; float subPathStartY = 0; char prevCmd = 0; while (ph.pos < n) { char cmd = pathString.charAt(ph.pos); switch (cmd) { case '-': case '+': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (prevCmd == 'm' || prevCmd == 'M') { cmd = (char) (((int) prevCmd) - 1); break; } else if (prevCmd == 'c' || prevCmd == 'C') { cmd = prevCmd; break; } else if (prevCmd == 'l' || prevCmd == 'L') { cmd = prevCmd; break; } default: { ph.advance(); prevCmd = cmd; } } boolean wasCurve = false; switch (cmd) { case 'M': case 'm': { float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 'm') { subPathStartX += x; subPathStartY += y; p.rMoveTo(x, y); lastX += x; lastY += y; } else { subPathStartX = x; subPathStartY = y; p.moveTo(x, y); lastX = x; lastY = y; } break; } case 'Z': case 'z': { p.close(); p.moveTo(subPathStartX, subPathStartY); lastX = subPathStartX; lastY = subPathStartY; lastX1 = subPathStartX; lastY1 = subPathStartY; wasCurve = true; break; } case 'L': case 'l': { float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 'l') { p.rLineTo(x, y); lastX += x; lastY += y; } else { p.lineTo(x, y); lastX = x; lastY = y; } break; } case 'H': case 'h': { float x = ph.nextFloat(); if (cmd == 'h') { p.rLineTo(x, 0); lastX += x; } else { p.lineTo(x, lastY); lastX = x; } break; } case 'V': case 'v': { float y = ph.nextFloat(); if (cmd == 'v') { p.rLineTo(0, y); lastY += y; } else { p.lineTo(lastX, y); lastY = y; } break; } case 'C': case 'c': { wasCurve = true; float x1 = ph.nextFloat(); float y1 = ph.nextFloat(); float x2 = ph.nextFloat(); float y2 = ph.nextFloat(); float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 'c') { x1 += lastX; x2 += lastX; x += lastX; y1 += lastY; y2 += lastY; y += lastY; } p.cubicTo(x1, y1, x2, y2, x, y); lastX1 = x2; lastY1 = y2; lastX = x; lastY = y; break; } case 'S': case 's': { wasCurve = true; float x2 = ph.nextFloat(); float y2 = ph.nextFloat(); float x = ph.nextFloat(); float y = ph.nextFloat(); if (cmd == 's') { x2 += lastX; x += lastX; y2 += lastY; y += lastY; } float x1 = 2 * lastX - lastX1; float y1 = 2 * lastY - lastY1; p.cubicTo(x1, y1, x2, y2, x, y); lastX1 = x2; lastY1 = y2; lastX = x; lastY = y; break; } case 'A': case 'a': { float rx = ph.nextFloat(); float ry = ph.nextFloat(); float theta = ph.nextFloat(); int largeArc = (int) ph.nextFloat(); int sweepArc = (int) ph.nextFloat(); float x = ph.nextFloat(); float y = ph.nextFloat(); drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc); lastX = x; lastY = y; break; } } if (!wasCurve) { lastX1 = lastX; lastY1 = lastY; } ph.skipWhitespace(); } }  static void parse(String pathString, Path p);  }
@Test public void testParseLineNext() throws Exception { PathParser.parse("l5,5+10,10", path); verify(path).rLineTo(5.0f, 5.0f); verify(path).rLineTo(10.0f, 10.0f); }
@Test public void testParseCurveS() throws Exception { PathParser.parse("S150,150,180,80s30,10,20,35", path); verify(path).cubicTo(0.0f, 0.0f, 150.0f, 150.0f, 180.0f, 80.0f); verify(path).cubicTo(210.0f, 10.0f, 210.0f, 90.0f, 200.0f, 115.0f); }
@Test public void testParseArc() throws Exception { PathParser.parse("A5,5,20,20,30,10,10", path); }
@Test public void testParseCubicPlus() throws Exception { PathParser.parse("c3,3,3,3,3,3+10,10,10,10,10,10", path); verify(path).cubicTo(3.0f, 3.0f, 3.0f, 3.0f, 3.0f, 3.0f); verify(path).cubicTo(13.0f, 13.0f, 13.0f, 13.0f, 13.0f, 13.0f); }
@Test public void testParseMoveNext() throws Exception { PathParser.parse("m1,2+10,23", path); verify(path).rMoveTo(1.0f, 2.0f); verify(path).rLineTo(10.0f, 23.0f); }
@Test public void testParseMove() throws Exception { PathParser.parse("M10,10m10,10", path); verify(path).moveTo(10.0f, 10.0f); verify(path).rMoveTo(10.0f, 10.0f); }
@Test public void testParseLine() throws Exception { PathParser.parse("ML10,10l0,10", path); verify(path).lineTo(10.0f, 10.0f); verify(path).rLineTo(0.0f, 10.0f); }
@Test public void testParseHLine() throws Exception { PathParser.parse("H10h5", path); verify(path).lineTo(10.0f,0.0f); verify(path).rLineTo(5.0f,0.0f); }
@Test public void testParseVLine() throws Exception { PathParser.parse("V10v5", path); verify(path).lineTo(0.0f,10.0f); verify(path).rLineTo(0.0f,5.0f); }
@Test public void testParseSquare() throws Exception { PathParser.parse("M10,10H90V90H10L10,10", path); verify(path, times(1)).moveTo(10.0f, 10.0f); verify(path).lineTo(90.0f, 10.0f); verify(path).lineTo(90.0f, 90.0f); verify(path).lineTo(10.0f, 90.0f); verify(path).lineTo(10.0f, 10.0f); }
@Test public void testParseTriangle() throws Exception { PathParser.parse("M250,150L150,350L350,350Z", path); verify(path, times(2)).moveTo(250.0f, 150.0f); verify(path).lineTo(150.0f, 350.0f); verify(path).lineTo(350.0f, 350.0f); verify(path).close(); }
@Test public void testParseCurve() throws Exception { PathParser.parse("C150,150,180,80,100,120c30,100,40,100,30,90", path); verify(path).cubicTo(150.0f, 150.0f, 180.0f, 80.0f, 100.0f, 120.0f); verify(path).cubicTo(130.0f, 220.0f, 140.0f, 220.0f, 130.0f, 210.0f); }