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