17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/*
27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * Copyright (C) 2014-2016, International Business Machines Corporation and
482027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer * others. All Rights Reserved.
57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert *******************************************************************************
67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl;
87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/**
1058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * Formats simple patterns like "{1} was born in {0}".
1158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * Minimal subset of MessageFormat; fast, simple, minimal dependencies.
1258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * Supports only numbered arguments with no type nor style parameters,
1358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * and formats only string values.
1458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * Quoting via ASCII apostrophe compatible with ICU MessageFormat default behavior.
1558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer *
1658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * <p>Factory methods throw exceptions for syntax errors
1758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * and for too few or too many arguments/placeholders.
1858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer *
1958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * <p>SimplePatternFormatter objects are immutable and can be safely cached like strings.
2058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer *
2158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * <p>Example:
227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre>
2358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * SimplePatternFormatter fmt = SimplePatternFormatter.compile("{1} '{born}' in {0}");
2458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer *
257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * // Output: "paul {born} in england"
267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * System.out.println(fmt.format("england", "paul"));
277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre>
2858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer *
2958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * @see com.ibm.icu.text.MessageFormat
3058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer * @see com.ibm.icu.text.MessagePattern.ApostropheMode
317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */
3258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Schererpublic final class SimplePatternFormatter {
339ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // For internal use in Java,
349ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // it is most efficient to compile patterns to compiled-pattern strings
359ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // and use them with static methods.
369ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    //
379ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // If and when we make this public API,
389ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // we should probably make only the non-static methods public
399ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // and keep the static ones in an impl class.
409ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    //
419ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // TODO: Consider changing methods & docs to use "argument" not "placeholder",
429ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer    // consistent with MessageFormat.
439ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer
4458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
4558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Argument numbers must be smaller than this limit.
4658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Text segment lengths are offset by this much.
4758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * This is currently the only unused char value in compiled patterns,
4858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * except it is the maximum value of the first unit (max arg +1).
4958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
5058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    private static final int ARG_NUM_LIMIT = 0x100;
5158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
5258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Initial and maximum char/UChar value set for a text segment.
5358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Segment length char values are from ARG_NUM_LIMIT+1 to this value here.
5458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Normally 0xffff, but can be as small as ARG_NUM_LIMIT+1 for testing.
5558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
5658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    private static final char SEGMENT_LENGTH_PLACEHOLDER_CHAR = (char)0xffff;
5758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
5858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Maximum length of a text segment. Longer segments are split into shorter ones.
5958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
6058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    private static final int MAX_SEGMENT_LENGTH = SEGMENT_LENGTH_PLACEHOLDER_CHAR - ARG_NUM_LIMIT;
617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
6258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
6358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Binary representation of the compiled pattern.
6458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Index 0: One more than the highest argument number.
6558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Followed by zero or more arguments or literal-text segments.
6658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
6758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * <p>An argument is stored as its number, less than ARG_NUM_LIMIT.
6858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * A literal-text segment is stored as its length (at least 1) offset by ARG_NUM_LIMIT,
6958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * followed by that many chars.
7058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
7158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    private final String compiledPattern;
7258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
7358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    private SimplePatternFormatter(String compiledPattern) {
7458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        this.compiledPattern = compiledPattern;
757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
7858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Creates a formatter from the pattern string.
7958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
8058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param pattern The pattern string.
8158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @return The new SimplePatternFormatter object.
827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
8358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static SimplePatternFormatter compile(CharSequence pattern) {
8482027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer        return compileMinMaxPlaceholders(pattern, 0, Integer.MAX_VALUE);
8582027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer    }
8682027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer
8782027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer    /**
8858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Creates a formatter from the pattern string.
8958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
9058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param pattern The pattern string.
9182027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer     * @param min The pattern must have at least this many placeholders.
9282027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer     * @param max The pattern must have at most this many placeholders.
9358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @return The new SimplePatternFormatter object.
9482027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer     */
9558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static SimplePatternFormatter compileMinMaxPlaceholders(CharSequence pattern, int min, int max) {
9658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        StringBuilder sb = new StringBuilder();
9758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        String compiledPattern = compileToStringMinMaxPlaceholders(pattern, sb, min, max);
9858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return new SimplePatternFormatter(compiledPattern);
9958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    }
10058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
10158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
10258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Creates a compiled form of the pattern string, for use with appropriate static methods.
10358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
10458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param pattern The pattern string.
10558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param min The pattern must have at least this many placeholders.
10658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param max The pattern must have at most this many placeholders.
10758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @return The compiled-pattern string.
10858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
10958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static String compileToStringMinMaxPlaceholders(
11058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            CharSequence pattern, StringBuilder sb, int min, int max) {
11158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // Parse consistent with MessagePattern, but
11258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // - support only simple numbered arguments
11358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // - build a simple binary structure into the result string
1149ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        int patternLength = pattern.length();
1159ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        sb.ensureCapacity(patternLength);
11658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // Reserve the first char for the number of arguments.
11758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        sb.setLength(1);
11858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        int textLength = 0;
11958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        int maxArg = -1;
12058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        boolean inQuote = false;
1219ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        for (int i = 0; i < patternLength;) {
12258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            char c = pattern.charAt(i++);
12358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            if (c == '\'') {
1249ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer                if (i < patternLength && (c = pattern.charAt(i)) == '\'') {
12558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    // double apostrophe, skip the second one
12658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    ++i;
12758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                } else if (inQuote) {
12858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    // skip the quote-ending apostrophe
12958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    inQuote = false;
13058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    continue;
13158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                } else if (c == '{' || c == '}') {
13258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    // Skip the quote-starting apostrophe, find the end of the quoted literal text.
13358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    ++i;
13458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    inQuote = true;
1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
13658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    // The apostrophe is part of literal text.
13758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    c = '\'';
1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
13958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            } else if (!inQuote && c == '{') {
14058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                if (textLength > 0) {
14158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    sb.setCharAt(sb.length() - textLength - 1, (char)(ARG_NUM_LIMIT + textLength));
14258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    textLength = 0;
1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
14458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                int argNumber;
1459ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer                if ((i + 1) < patternLength &&
14658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        0 <= (argNumber = pattern.charAt(i) - '0') && argNumber <= 9 &&
14758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        pattern.charAt(i + 1) == '}') {
14858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    i += 2;
1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                } else {
15058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    // Multi-digit argument number (no leading zero) or syntax error.
15158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    // MessagePattern permits PatternProps.skipWhiteSpace(pattern, index)
15258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    // around the number, but this class does not.
15358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    int argStart = i - 1;
15458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    argNumber = -1;
1559ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer                    if (i < patternLength && '1' <= (c = pattern.charAt(i++)) && c <= '9') {
15658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        argNumber = c - '0';
1579ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer                        while (i < patternLength && '0' <= (c = pattern.charAt(i++)) && c <= '9') {
15858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                            argNumber = argNumber * 10 + (c - '0');
15958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                            if (argNumber >= ARG_NUM_LIMIT) {
16058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                                break;
16158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                            }
16258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        }
16358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    }
16458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    if (argNumber < 0 || c != '}') {
16558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        throw new IllegalArgumentException(
16658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                                "Argument syntax error in pattern \"" + pattern +
16758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                                "\" at index " + argStart +
16858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                                ": " + pattern.subSequence(argStart, i));
16958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    }
17058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                }
17158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                if (argNumber > maxArg) {
17258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    maxArg = argNumber;
1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert                }
17458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                sb.append((char)argNumber);
17558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                continue;
17658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            }  // else: c is part of literal text
17758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            // Append c and track the literal-text segment length.
17858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            if (textLength == 0) {
17958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                // Reserve a char for the length of a new text segment, preset the maximum length.
18058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                sb.append(SEGMENT_LENGTH_PLACEHOLDER_CHAR);
18158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            }
18258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            sb.append(c);
18358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            if (++textLength == MAX_SEGMENT_LENGTH) {
18458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                textLength = 0;
1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
18758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        if (textLength > 0) {
18858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            sb.setCharAt(sb.length() - textLength - 1, (char)(ARG_NUM_LIMIT + textLength));
1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
19058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        int argCount = maxArg + 1;
19158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        if (argCount < min) {
19282027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer            throw new IllegalArgumentException(
19382027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer                    "Fewer than minimum " + min + " placeholders in pattern \"" + pattern + "\"");
19482027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer        }
19558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        if (argCount > max) {
19682027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer            throw new IllegalArgumentException(
19782027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer                    "More than maximum " + max + " placeholders in pattern \"" + pattern + "\"");
19882027afe36d2dbe419417f025716dc57c89ee0a4Markus Scherer        }
19958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        sb.setCharAt(0, (char)argCount);
20058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return sb.toString();
2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
20258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
20458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @return The max argument number/placeholder ID + 1.
2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public int getPlaceholderCount() {
20758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return getPlaceholderCount(compiledPattern);
20858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    }
20958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
21058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
21158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param compiledPattern Compiled form of a pattern string.
21258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @return The max argument number/placeholder ID + 1.
21358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
21458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static int getPlaceholderCount(String compiledPattern) {
21558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return compiledPattern.charAt(0);
2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
21758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Formats the given values.
2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    public String format(CharSequence... values) {
22258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return formatCompiledPattern(compiledPattern, values);
2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    /**
2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * Formats the given values.
22758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
22858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param compiledPattern Compiled form of a pattern string.
22958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
23058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static String formatCompiledPattern(String compiledPattern, CharSequence... values) {
23158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return formatAndAppend(compiledPattern, new StringBuilder(), null, values).toString();
23258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    }
23358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
23458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
23558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Formats the given values, appending to the appendTo builder.
23658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
23758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param appendTo Gets the formatted pattern and values appended.
23858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param offsets offsets[i] receives the offset of where
23958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                values[i] replaced pattern argument {i}.
24058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                Can be null, or can be shorter or longer than values.
24158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                If there is no {i} in the pattern, then offsets[i] is set to -1.
24258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param values The placeholder values.
2439ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               A placeholder value must not be the same object as appendTo.
2449ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               values.length must be at least getPlaceholderCount().
2459ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               Can be null if getPlaceholderCount()==0.
2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     * @return appendTo
2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert     */
248f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public StringBuilder formatAndAppend(
2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            StringBuilder appendTo, int[] offsets, CharSequence... values) {
25058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return formatAndAppend(compiledPattern, appendTo, offsets, values);
25158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    }
25258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
25358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
25458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Formats the given values, appending to the appendTo builder.
25558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
25658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param compiledPattern Compiled form of a pattern string.
25758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param appendTo Gets the formatted pattern and values appended.
25858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param offsets offsets[i] receives the offset of where
25958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                values[i] replaced pattern argument {i}.
26058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                Can be null, or can be shorter or longer than values.
26158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                If there is no {i} in the pattern, then offsets[i] is set to -1.
26258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param values The placeholder values.
2639ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               A placeholder value must not be the same object as appendTo.
2649ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               values.length must be at least getPlaceholderCount().
2659ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               Can be null if getPlaceholderCount()==0.
26658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @return appendTo
26758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
26858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static StringBuilder formatAndAppend(
26958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            String compiledPattern, StringBuilder appendTo, int[] offsets, CharSequence... values) {
2709ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        int valuesLength = values != null ? values.length : 0;
2719ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        if (valuesLength < getPlaceholderCount(compiledPattern)) {
2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            throw new IllegalArgumentException("Too few values.");
2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
2749ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        return format(compiledPattern, values, appendTo, null, true, offsets);
275f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
27658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
277f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
27858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Formats the given values, replacing the contents of the result builder.
27958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * May optimize by actually appending to the result if it is the same object
28058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * as the initial argument's corresponding value.
28158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
2829ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     * @param result Gets its contents replaced by the formatted pattern and values.
28358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param offsets offsets[i] receives the offset of where
28458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                values[i] replaced pattern argument {i}.
28558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                Can be null, or can be shorter or longer than values.
28658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                If there is no {i} in the pattern, then offsets[i] is set to -1.
28758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param values The placeholder values.
28858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *               A placeholder value may be the same object as result.
2899ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               values.length must be at least getPlaceholderCount().
290f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     * @return result
291f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
292f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public StringBuilder formatAndReplace(
293f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            StringBuilder result, int[] offsets, CharSequence... values) {
29458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return formatAndReplace(compiledPattern, result, offsets, values);
29558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    }
29658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
29758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    /**
29858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Formats the given values, replacing the contents of the result builder.
29958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * May optimize by actually appending to the result if it is the same object
30058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * as the initial argument's corresponding value.
30158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
30258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param compiledPattern Compiled form of a pattern string.
3039ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     * @param result Gets its contents replaced by the formatted pattern and values.
30458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param offsets offsets[i] receives the offset of where
30558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                values[i] replaced pattern argument {i}.
30658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                Can be null, or can be shorter or longer than values.
30758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *                If there is no {i} in the pattern, then offsets[i] is set to -1.
30858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param values The placeholder values.
30958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *               A placeholder value may be the same object as result.
3109ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer     *               values.length must be at least getPlaceholderCount().
31158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @return result
31258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     */
31358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static StringBuilder formatAndReplace(
31458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            String compiledPattern, StringBuilder result, int[] offsets, CharSequence... values) {
3159ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        int valuesLength = values != null ? values.length : 0;
3169ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        if (valuesLength < getPlaceholderCount(compiledPattern)) {
317f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            throw new IllegalArgumentException("Too few values.");
318f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
31958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
32058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // If the pattern starts with an argument whose value is the same object
32158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // as the result, then we keep the result contents and append to it.
32258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // Otherwise we replace its contents.
32358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        int firstArg = -1;
32458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // If any non-initial argument value is the same object as the result,
32558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        // then we first copy its contents and use that instead while formatting.
32658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        String resultCopy = null;
32758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        if (getPlaceholderCount(compiledPattern) > 0) {
32858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            for (int i = 1; i < compiledPattern.length();) {
32958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                int n = compiledPattern.charAt(i++);
33058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                if (n < ARG_NUM_LIMIT) {
33158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    if (values[n] == result) {
33258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        if (i == 2) {
33358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                            firstArg = n;
33458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        } else if (resultCopy == null) {
33558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                            resultCopy = result.toString();
33658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        }
33758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    }
33858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                } else {
33958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    i += n - ARG_NUM_LIMIT;
34058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                }
341f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            }
342f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
34358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        if (firstArg < 0) {
34458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            result.setLength(0);
345f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
3469ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer        return format(compiledPattern, values, result, resultCopy, false, offsets);
347f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
34858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
349f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
35058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Returns a string similar to the original pattern, only for debugging.
351f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
352f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    @Override
353f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    public String toString() {
35458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        String[] values = new String[getPlaceholderCount()];
355f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        for (int i = 0; i < values.length; i++) {
356f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            values[i] = String.format("{%d}", i);
357f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
358f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        return formatAndAppend(new StringBuilder(), null, values).toString();
359f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
36058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
361f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
36258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Returns the pattern text with none of the placeholders.
36358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Like formatting with all-empty string values.
364f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
36558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public String getTextWithNoPlaceholders() {
36658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return getTextWithNoPlaceholders(compiledPattern);
367f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    }
36858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer
369f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert    /**
37058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Returns the pattern text with none of the placeholders.
37158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * Like formatting with all-empty string values.
37258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     *
37358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer     * @param compiledPattern Compiled form of a pattern string.
374f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert     */
37558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    public static String getTextWithNoPlaceholders(String compiledPattern) {
37658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        int capacity = compiledPattern.length() - 1 - getPlaceholderCount(compiledPattern);
37758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        StringBuilder sb = new StringBuilder(capacity);
37858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        for (int i = 1; i < compiledPattern.length();) {
37958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            int segmentLength = compiledPattern.charAt(i++) - ARG_NUM_LIMIT;
38058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            if (segmentLength > 0) {
38158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                int limit = i + segmentLength;
38258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                sb.append(compiledPattern, i, limit);
38358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                i = limit;
384f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            }
3857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
38658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return sb.toString();
3877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
3887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert
38958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer    private static StringBuilder format(
3909ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer            String compiledPattern, CharSequence[] values,
39158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            StringBuilder result, String resultCopy, boolean forbidResultAsValue,
3929ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer            int[] offsets) {
39358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        int offsetsLength;
39458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        if (offsets == null) {
39558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            offsetsLength = 0;
39658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        } else {
39758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            offsetsLength = offsets.length;
39858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            for (int i = 0; i < offsetsLength; i++) {
39958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                offsets[i] = -1;
4007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert            }
4017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert        }
40258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        for (int i = 1; i < compiledPattern.length();) {
40358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            int n = compiledPattern.charAt(i++);
40458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            if (n < ARG_NUM_LIMIT) {
4059ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer                CharSequence value = values[n];
4069ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer                if (value == result) {
40758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    if (forbidResultAsValue) {
40858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        throw new IllegalArgumentException("Value must not be same object as result");
40958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    }
41058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    if (i == 2) {
41158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        // We are appending to result which is also the first value object.
41258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        if (n < offsetsLength) {
41358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                            offsets[n] = 0;
41458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        }
41558d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    } else {
41658d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        if (n < offsetsLength) {
41758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                            offsets[n] = result.length();
41858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        }
41958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        result.append(resultCopy);
42058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    }
42158d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                } else {
42258d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    if (n < offsetsLength) {
42358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                        offsets[n] = result.length();
42458d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                    }
4259ed78814084b919478a4e43666f80824bc96d38bMarkus Scherer                    result.append(value);
426f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert                }
42758d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer            } else {
42858d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                int limit = i + (n - ARG_NUM_LIMIT);
42958d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                result.append(compiledPattern, i, limit);
43058d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer                i = limit;
431f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert            }
432f716bda031dccdec5e47bb40e758c5901d209729Fredrik Roubert        }
43358d9241f85e1d50255a8ee2dc4e85bd33097acabMarkus Scherer        return result;
4347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert    }
4357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert}
436