@Test public void testParse() { assertEquals("%d", JsFormat.parse("%d")); assertEquals("23", JsFormat.parse("%d", 23)); assertEquals("2", JsFormat.parse("%d", 2.7)); assertEquals("23.37", JsFormat.parse("%.2f", 23.3678)); assertEquals("hellow world", JsFormat.parse("hellow %s", "world")); assertEquals("%$2d dp 5 1 2 3 4", JsFormat.parse("%$2d dp 5", 1, 2, 3 ,4)); assertEquals("2 dp 4 1 3 4", JsFormat.parse("%2$d dp 4", 1, 2, 3 ,4)); assertEquals("NaN dp 4 one three four", JsFormat.parse("%2$d dp 4", "one", "two", "three", "four")); assertEquals("two dp 4 one three four", JsFormat.parse("%2$s dp 4", "one", "two", "three", "four")); assertEquals("two vs two one three four", JsFormat.parse("%2$s vs %2$s", "one", "two", "three", "four")); assertEquals("two vs two one three four", JsFormat.parse("%2$s vs %2$s", "one", "two", "three", "four")); assertEquals("two vs four one three", JsFormat.parse("%2$s vs %4$s", "one", "two", "three", "four")); assertEquals("two vs four %.3$4f one three 1234.5678", JsFormat.parse("%2$s vs %4$s %.3$4f", "one", "two", "three", "four", 1234.5678)); assertEquals("two vs four %.2$4f one three 1234.5678", JsFormat.parse("%2$s vs %4$s %.2$4f", "one", "two", "three", "four", 1234.5678)); assertEquals("two vs four 1234.57 one three", JsFormat.parse("%2$s vs %4$s %.2f", "one", "two", "three", "four", 1234.5678)); assertEquals("1234.57", JsFormat.parse("%.2f", 1234.5678)); assertEquals("%,2f 1234.5678", JsFormat.parse("%,2f", 1234.5678)); assertEquals("1234.57", JsFormat.parse("%.2f", 1234.5678)); assertEquals("NaN", JsFormat.parse("%.2f", "hello")); assertEquals("%.2a hello", JsFormat.parse("%.2a", "hello")); assertEquals("% cool .2a hello", JsFormat.parse("%% cool .2a", "hello")); assertEquals("two vs four 1234.5678 one three", JsFormat.parse("%2$s vs %4$s %s", "one", "two", "three", "four", 1234.5678)); assertEquals("two vs four %s", JsFormat.parse("two vs four %s")); assertEquals("two vs four % one three", JsFormat.parse("two vs four %", "one", "three")); assertEquals("two one two four oops three", JsFormat.parse("%2$s %1$s %s %4$s %s", "one", "two", "three", "four", "oops")); }
static @NonNull String parse(@NonNull Object...args) { boolean[] argsUsed = new boolean[args.length]; String format = (String) args[0]; argsUsed[0] = true; int nextArgIndex = 1; final char[] chars = format.toCharArray(); StringBuilder buffer = new StringBuilder(); ArrayCharSequence sequence = new ArrayCharSequence(chars, 0, chars.length); for (int i = 0; i < chars.length; ++i) { char c = chars[i]; if (c != '%') { buffer.append(c); continue; } Matcher matcher = FORMAT_SPECIFIER_PATTERN.matcher(sequence.substring(i)); if (!matcher.find()) { if (i + 1 < chars.length) { char peek = chars[i + 1]; if (peek == '%') { ++i; } } buffer.append('%'); continue; } int groupCount = matcher.groupCount(); int index = -1; int width = -1; int precision = -1; char conversion = 0; for (int groupIdx = 1; groupIdx <= groupCount; ++groupIdx) { String value = matcher.group(groupIdx); if (value == null || value.equals("")) { continue; } if (value.endsWith("$")) { value = value.substring(0, value.length() - 1); index = Integer.parseInt(value); continue; } char first = value.charAt(0); if (first == '.') { value = value.substring(1); precision = Integer.parseInt(value); } else if (first >= '0' && first <= '9') { width = Integer.parseInt(value); } else { conversion = first; } } String currentFormat = matcher.group(); final Object value; final boolean found; if (index > argsUsed.length || (width > -1 && index == -1)) { value = null; found = false; } else if (index <= argsUsed.length && index > 0) { value = args[index]; argsUsed[index] = true; nextArgIndex = index + 1; found = true; } else { if (nextArgIndex < argsUsed.length) { value = args[nextArgIndex]; argsUsed[nextArgIndex] = true; ++nextArgIndex; found = true; } else { value = null; found = false; } } if (!found) { buffer.append(currentFormat); i += currentFormat.length() - 1; continue; } switch (conversion) { case 'd': case 'i': Object l; if (value instanceof String) { try { l = Long.parseLong((String) value); } catch (NumberFormatException e) { l = "NaN"; } } else if (value instanceof Number) { l = ((Number) value).intValue(); } else { l = 0; } buffer.append(l); break; case 'f': Object d; if (value instanceof String) { try { d = Double.parseDouble((String) value); } catch (NumberFormatException e) { d = "NaN"; } } else if (value instanceof Number) { d = ((Number) value).doubleValue(); } else { d = 0; } if (precision > -1 && d instanceof Number) { d = String.format(Locale.US, "%." + precision + "f", d); } buffer.append(d); break; case 's': default: buffer.append(value); break; } i += currentFormat.length() - 1; } for (int j = 0; j < argsUsed.length; j++) { boolean argUsed = argsUsed[j]; if (!argUsed) { buffer.append(" "); buffer.append(args[j]); } } return buffer.toString(); }
JsFormat { static @NonNull String parse(@NonNull Object...args) { boolean[] argsUsed = new boolean[args.length]; String format = (String) args[0]; argsUsed[0] = true; int nextArgIndex = 1; final char[] chars = format.toCharArray(); StringBuilder buffer = new StringBuilder(); ArrayCharSequence sequence = new ArrayCharSequence(chars, 0, chars.length); for (int i = 0; i < chars.length; ++i) { char c = chars[i]; if (c != '%') { buffer.append(c); continue; } Matcher matcher = FORMAT_SPECIFIER_PATTERN.matcher(sequence.substring(i)); if (!matcher.find()) { if (i + 1 < chars.length) { char peek = chars[i + 1]; if (peek == '%') { ++i; } } buffer.append('%'); continue; } int groupCount = matcher.groupCount(); int index = -1; int width = -1; int precision = -1; char conversion = 0; for (int groupIdx = 1; groupIdx <= groupCount; ++groupIdx) { String value = matcher.group(groupIdx); if (value == null || value.equals("")) { continue; } if (value.endsWith("$")) { value = value.substring(0, value.length() - 1); index = Integer.parseInt(value); continue; } char first = value.charAt(0); if (first == '.') { value = value.substring(1); precision = Integer.parseInt(value); } else if (first >= '0' && first <= '9') { width = Integer.parseInt(value); } else { conversion = first; } } String currentFormat = matcher.group(); final Object value; final boolean found; if (index > argsUsed.length || (width > -1 && index == -1)) { value = null; found = false; } else if (index <= argsUsed.length && index > 0) { value = args[index]; argsUsed[index] = true; nextArgIndex = index + 1; found = true; } else { if (nextArgIndex < argsUsed.length) { value = args[nextArgIndex]; argsUsed[nextArgIndex] = true; ++nextArgIndex; found = true; } else { value = null; found = false; } } if (!found) { buffer.append(currentFormat); i += currentFormat.length() - 1; continue; } switch (conversion) { case 'd': case 'i': Object l; if (value instanceof String) { try { l = Long.parseLong((String) value); } catch (NumberFormatException e) { l = "NaN"; } } else if (value instanceof Number) { l = ((Number) value).intValue(); } else { l = 0; } buffer.append(l); break; case 'f': Object d; if (value instanceof String) { try { d = Double.parseDouble((String) value); } catch (NumberFormatException e) { d = "NaN"; } } else if (value instanceof Number) { d = ((Number) value).doubleValue(); } else { d = 0; } if (precision > -1 && d instanceof Number) { d = String.format(Locale.US, "%." + precision + "f", d); } buffer.append(d); break; case 's': default: buffer.append(value); break; } i += currentFormat.length() - 1; } for (int j = 0; j < argsUsed.length; j++) { boolean argUsed = argsUsed[j]; if (!argUsed) { buffer.append(" "); buffer.append(args[j]); } } return buffer.toString(); } }
JsFormat { static @NonNull String parse(@NonNull Object...args) { boolean[] argsUsed = new boolean[args.length]; String format = (String) args[0]; argsUsed[0] = true; int nextArgIndex = 1; final char[] chars = format.toCharArray(); StringBuilder buffer = new StringBuilder(); ArrayCharSequence sequence = new ArrayCharSequence(chars, 0, chars.length); for (int i = 0; i < chars.length; ++i) { char c = chars[i]; if (c != '%') { buffer.append(c); continue; } Matcher matcher = FORMAT_SPECIFIER_PATTERN.matcher(sequence.substring(i)); if (!matcher.find()) { if (i + 1 < chars.length) { char peek = chars[i + 1]; if (peek == '%') { ++i; } } buffer.append('%'); continue; } int groupCount = matcher.groupCount(); int index = -1; int width = -1; int precision = -1; char conversion = 0; for (int groupIdx = 1; groupIdx <= groupCount; ++groupIdx) { String value = matcher.group(groupIdx); if (value == null || value.equals("")) { continue; } if (value.endsWith("$")) { value = value.substring(0, value.length() - 1); index = Integer.parseInt(value); continue; } char first = value.charAt(0); if (first == '.') { value = value.substring(1); precision = Integer.parseInt(value); } else if (first >= '0' && first <= '9') { width = Integer.parseInt(value); } else { conversion = first; } } String currentFormat = matcher.group(); final Object value; final boolean found; if (index > argsUsed.length || (width > -1 && index == -1)) { value = null; found = false; } else if (index <= argsUsed.length && index > 0) { value = args[index]; argsUsed[index] = true; nextArgIndex = index + 1; found = true; } else { if (nextArgIndex < argsUsed.length) { value = args[nextArgIndex]; argsUsed[nextArgIndex] = true; ++nextArgIndex; found = true; } else { value = null; found = false; } } if (!found) { buffer.append(currentFormat); i += currentFormat.length() - 1; continue; } switch (conversion) { case 'd': case 'i': Object l; if (value instanceof String) { try { l = Long.parseLong((String) value); } catch (NumberFormatException e) { l = "NaN"; } } else if (value instanceof Number) { l = ((Number) value).intValue(); } else { l = 0; } buffer.append(l); break; case 'f': Object d; if (value instanceof String) { try { d = Double.parseDouble((String) value); } catch (NumberFormatException e) { d = "NaN"; } } else if (value instanceof Number) { d = ((Number) value).doubleValue(); } else { d = 0; } if (precision > -1 && d instanceof Number) { d = String.format(Locale.US, "%." + precision + "f", d); } buffer.append(d); break; case 's': default: buffer.append(value); break; } i += currentFormat.length() - 1; } for (int j = 0; j < argsUsed.length; j++) { boolean argUsed = argsUsed[j]; if (!argUsed) { buffer.append(" "); buffer.append(args[j]); } } return buffer.toString(); }  }
JsFormat { static @NonNull String parse(@NonNull Object...args) { boolean[] argsUsed = new boolean[args.length]; String format = (String) args[0]; argsUsed[0] = true; int nextArgIndex = 1; final char[] chars = format.toCharArray(); StringBuilder buffer = new StringBuilder(); ArrayCharSequence sequence = new ArrayCharSequence(chars, 0, chars.length); for (int i = 0; i < chars.length; ++i) { char c = chars[i]; if (c != '%') { buffer.append(c); continue; } Matcher matcher = FORMAT_SPECIFIER_PATTERN.matcher(sequence.substring(i)); if (!matcher.find()) { if (i + 1 < chars.length) { char peek = chars[i + 1]; if (peek == '%') { ++i; } } buffer.append('%'); continue; } int groupCount = matcher.groupCount(); int index = -1; int width = -1; int precision = -1; char conversion = 0; for (int groupIdx = 1; groupIdx <= groupCount; ++groupIdx) { String value = matcher.group(groupIdx); if (value == null || value.equals("")) { continue; } if (value.endsWith("$")) { value = value.substring(0, value.length() - 1); index = Integer.parseInt(value); continue; } char first = value.charAt(0); if (first == '.') { value = value.substring(1); precision = Integer.parseInt(value); } else if (first >= '0' && first <= '9') { width = Integer.parseInt(value); } else { conversion = first; } } String currentFormat = matcher.group(); final Object value; final boolean found; if (index > argsUsed.length || (width > -1 && index == -1)) { value = null; found = false; } else if (index <= argsUsed.length && index > 0) { value = args[index]; argsUsed[index] = true; nextArgIndex = index + 1; found = true; } else { if (nextArgIndex < argsUsed.length) { value = args[nextArgIndex]; argsUsed[nextArgIndex] = true; ++nextArgIndex; found = true; } else { value = null; found = false; } } if (!found) { buffer.append(currentFormat); i += currentFormat.length() - 1; continue; } switch (conversion) { case 'd': case 'i': Object l; if (value instanceof String) { try { l = Long.parseLong((String) value); } catch (NumberFormatException e) { l = "NaN"; } } else if (value instanceof Number) { l = ((Number) value).intValue(); } else { l = 0; } buffer.append(l); break; case 'f': Object d; if (value instanceof String) { try { d = Double.parseDouble((String) value); } catch (NumberFormatException e) { d = "NaN"; } } else if (value instanceof Number) { d = ((Number) value).doubleValue(); } else { d = 0; } if (precision > -1 && d instanceof Number) { d = String.format(Locale.US, "%." + precision + "f", d); } buffer.append(d); break; case 's': default: buffer.append(value); break; } i += currentFormat.length() - 1; } for (int j = 0; j < argsUsed.length; j++) { boolean argUsed = argsUsed[j]; if (!argUsed) { buffer.append(" "); buffer.append(args[j]); } } return buffer.toString(); }   }
JsFormat { static @NonNull String parse(@NonNull Object...args) { boolean[] argsUsed = new boolean[args.length]; String format = (String) args[0]; argsUsed[0] = true; int nextArgIndex = 1; final char[] chars = format.toCharArray(); StringBuilder buffer = new StringBuilder(); ArrayCharSequence sequence = new ArrayCharSequence(chars, 0, chars.length); for (int i = 0; i < chars.length; ++i) { char c = chars[i]; if (c != '%') { buffer.append(c); continue; } Matcher matcher = FORMAT_SPECIFIER_PATTERN.matcher(sequence.substring(i)); if (!matcher.find()) { if (i + 1 < chars.length) { char peek = chars[i + 1]; if (peek == '%') { ++i; } } buffer.append('%'); continue; } int groupCount = matcher.groupCount(); int index = -1; int width = -1; int precision = -1; char conversion = 0; for (int groupIdx = 1; groupIdx <= groupCount; ++groupIdx) { String value = matcher.group(groupIdx); if (value == null || value.equals("")) { continue; } if (value.endsWith("$")) { value = value.substring(0, value.length() - 1); index = Integer.parseInt(value); continue; } char first = value.charAt(0); if (first == '.') { value = value.substring(1); precision = Integer.parseInt(value); } else if (first >= '0' && first <= '9') { width = Integer.parseInt(value); } else { conversion = first; } } String currentFormat = matcher.group(); final Object value; final boolean found; if (index > argsUsed.length || (width > -1 && index == -1)) { value = null; found = false; } else if (index <= argsUsed.length && index > 0) { value = args[index]; argsUsed[index] = true; nextArgIndex = index + 1; found = true; } else { if (nextArgIndex < argsUsed.length) { value = args[nextArgIndex]; argsUsed[nextArgIndex] = true; ++nextArgIndex; found = true; } else { value = null; found = false; } } if (!found) { buffer.append(currentFormat); i += currentFormat.length() - 1; continue; } switch (conversion) { case 'd': case 'i': Object l; if (value instanceof String) { try { l = Long.parseLong((String) value); } catch (NumberFormatException e) { l = "NaN"; } } else if (value instanceof Number) { l = ((Number) value).intValue(); } else { l = 0; } buffer.append(l); break; case 'f': Object d; if (value instanceof String) { try { d = Double.parseDouble((String) value); } catch (NumberFormatException e) { d = "NaN"; } } else if (value instanceof Number) { d = ((Number) value).doubleValue(); } else { d = 0; } if (precision > -1 && d instanceof Number) { d = String.format(Locale.US, "%." + precision + "f", d); } buffer.append(d); break; case 's': default: buffer.append(value); break; } i += currentFormat.length() - 1; } for (int j = 0; j < argsUsed.length; j++) { boolean argUsed = argsUsed[j]; if (!argUsed) { buffer.append(" "); buffer.append(args[j]); } } return buffer.toString(); }    }