DefaultPrintfMessageParser extends PrintfMessageParser { @Override public int parsePrintfTerm( MessageBuilder<?> builder, int index, String message, int termStart, int specStart, int formatStart) throws ParseException { int termEnd = formatStart + 1; char typeChar = message.charAt(formatStart); boolean isUpperCase = (typeChar & 0x20) == 0; FormatOptions options = FormatOptions.parse(message, specStart, formatStart, isUpperCase); Parameter parameter; FormatChar formatChar = FormatChar.of(typeChar); if (formatChar != null) { if (!options.areValidFor(formatChar)) { throw ParseException.withBounds("invalid format specifier", message, termStart, termEnd); } parameter = SimpleParameter.of(index, formatChar, options); } else if (typeChar == 't' || typeChar == 'T') { if (!options.validate(FLAG_LEFT_ALIGN | FLAG_UPPER_CASE, false)) { throw ParseException.withBounds( "invalid format specification", message, termStart, termEnd); } termEnd += 1; if (termEnd > message.length()) { throw ParseException.atPosition("truncated format specifier", message, termStart); } DateTimeFormat dateTimeFormat = DateTimeFormat.of(message.charAt(formatStart + 1)); if (dateTimeFormat == null) { throw ParseException.atPosition("illegal date/time conversion", message, formatStart + 1); } parameter = DateTimeParameter.of(dateTimeFormat, options, index); } else if (typeChar == 'h' || typeChar == 'H') { if (!options.validate(FLAG_LEFT_ALIGN | FLAG_UPPER_CASE, false)) { throw ParseException.withBounds( "invalid format specification", message, termStart, termEnd); } parameter = wrapHexParameter(options, index); } else { throw ParseException.withBounds( "invalid format specification", message, termStart, formatStart + 1); } builder.addParameter(termStart, termEnd, parameter); return termEnd; } private  DefaultPrintfMessageParser(); static PrintfMessageParser getInstance(); @Override int parsePrintfTerm(
      MessageBuilder<?> builder,
      int index,
      String message,
      int termStart,
      int specStart,
      int formatStart);  }
@Test public void testParsePrintf() throws ParseException { MessageBuilder<?> builder = mock(MessageBuilder.class); int unused = PARSER.parsePrintfTerm(builder, 1, "Hello %2$+06.2f World", 6, 9, 14); ArgumentCaptor<Parameter> param = ArgumentCaptor.forClass(Parameter.class); verify(builder).addParameterImpl(eq(6), eq(15), param.capture()); assertThat(param.getValue().getIndex()).isEqualTo(1); ParameterVisitor out = mock(ParameterVisitor.class); param.getValue().accept(out, new Object[] {"Answer: ", 42.0}); ArgumentCaptor<FormatOptions> options = ArgumentCaptor.forClass(FormatOptions.class); verify(out).visit(eq(42.0), eq(FormatChar.FLOAT), options.capture()); assertThat(options.getValue().getWidth()).isEqualTo(6); assertThat(options.getValue().getPrecision()).isEqualTo(2); assertThat(options.getValue().shouldShowLeadingZeros()).isTrue(); assertThat(options.getValue().shouldPrefixPlusForPositiveValues()).isTrue(); }
@Test public void testUnknownPrintfFormat() { try { int unused = PARSER.parsePrintfTerm(null, 0, "%Q", 0, 1, 1); fail("expected ParseException"); } catch (ParseException expected) { assertThat(expected.getMessage()).contains("[%Q]"); } }
@Test public void testInvalidPrintfFlags() { try { int unused = PARSER.parsePrintfTerm(null, 0, "%0s", 0, 1, 2); fail("expected ParseException"); } catch (ParseException expected) { assertThat(expected.getMessage()).contains("[%0s]"); } }