UrlUtil { protected static String decode(String url) { StringBuilder stringBuilder = new StringBuilder(url); Stack<Integer> nonDecodedPercentIndices = new Stack<Integer>(); int i = 0; while (i < stringBuilder.length() - 2) { char curr = stringBuilder.charAt(i); if (curr == '%') { if (CharUtils.isHex(stringBuilder.charAt(i + 1)) && CharUtils.isHex(stringBuilder.charAt(i + 2))) { char decodedChar = String.format("%s", (char) Short.parseShort(stringBuilder.substring(i + 1, i + 3), 16)).charAt(0); stringBuilder.delete(i, i + 3); stringBuilder.insert(i, decodedChar); if (decodedChar == '%') { i--; } else if (!nonDecodedPercentIndices.isEmpty() && CharUtils.isHex(decodedChar) && CharUtils.isHex(stringBuilder.charAt(i - 1)) && i - nonDecodedPercentIndices.peek() == 2) { i = nonDecodedPercentIndices.pop() - 1; } else if (!nonDecodedPercentIndices.isEmpty() && i == stringBuilder.length() - 2) { i = nonDecodedPercentIndices.pop() - 1; } } else { nonDecodedPercentIndices.add(i); } } i++; } return stringBuilder.toString(); } private  UrlUtil();   }
@Test(dataProvider = "getDecodeStrings") public void testDecode(String input, String expectedDecodedString) { Assert.assertEquals(UrlUtil.decode(input), expectedDecodedString); }