SimplePatternFormatter.java revision 7935b1839a081ed19ae0d33029ad3c09632a2caa
17935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/* 27935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 37935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Copyright (C) 2014, International Business Machines Corporation and * 47935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * others. All Rights Reserved. * 57935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert ******************************************************************************* 67935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 77935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpackage com.ibm.icu.impl; 87935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 97935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.ArrayList; 107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertimport java.util.List; 117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert/** 137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Compiled version of a pattern such as "{1} was born in {0}". 147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Using SimplePatternFormatter objects is both faster and safer than adhoc replacement 167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * such as <code>pattern.replace("{0}", "Colorado").replace("{1} "Fred");</code>. 177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * They are faster because they are precompiled; they are safer because they 187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * account for curly braces escaped by apostrophe ('). 197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Placeholders are of the form \{[0-9]+\}. If a curly brace is preceded 217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * by a single quote, it becomes a curly brace instead of the start of a 227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * placeholder. Two single quotes resolve to one single quote. 237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * SimplePatternFormatter objects are immutable and can be safely cached like strings. 257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <p> 267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Example: 277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * <pre> 287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * SimplePatternFormatter fmt = SimplePatternFormatter.compile("{1} '{born} in {0}"); 297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * // Output: "paul {born} in england" 317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * System.out.println(fmt.format("england", "paul")); 327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * </pre> 337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubertpublic class SimplePatternFormatter { 357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private final String patternWithoutPlaceholders; 367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private final int placeholderCount; 377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // [0] first offset; [1] first placeholderId; [2] second offset; 397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert // [3] second placeholderId etc. 407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private final int[] placeholderIdsOrderedByOffset; 417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private SimplePatternFormatter(String pattern, PlaceholdersBuilder builder) { 437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.patternWithoutPlaceholders = pattern; 447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.placeholderIdsOrderedByOffset = 457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert builder.getPlaceholderIdsOrderedByOffset(); 467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert this.placeholderCount = builder.getPlaceholderCount(); 477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Compiles a string. 517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param pattern The string. 527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return the new SimplePatternFormatter object. 537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public static SimplePatternFormatter compile(CharSequence pattern) { 557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert PlaceholdersBuilder placeholdersBuilder = new PlaceholdersBuilder(); 567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert PlaceholderIdBuilder idBuilder = new PlaceholderIdBuilder(); 577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder newPattern = new StringBuilder(); 587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert State state = State.INIT; 597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0; i < pattern.length(); i++) { 607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert char ch = pattern.charAt(i); 617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (state) { 627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case INIT: 637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (ch == 0x27) { 647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = State.APOSTROPHE; 657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (ch == '{') { 667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = State.PLACEHOLDER; 677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert idBuilder.reset(); 687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append(ch); 707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case APOSTROPHE: 737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (ch == 0x27) { 747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append("'"); 757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (ch == '{') { 767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append("{"); 777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append("'"); 797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append(ch); 807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = State.INIT; 827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case PLACEHOLDER: 847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (ch >= '0' && ch <= '9') { 857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert idBuilder.add(ch); 867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else if (ch == '}' && idBuilder.isValid()) { 877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholdersBuilder.add(idBuilder.getId(), newPattern.length()); 887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = State.INIT; 897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append('{'); 917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert idBuilder.appendTo(newPattern); 927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append(ch); 937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert state = State.INIT; 947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: 977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalStateException(); 987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert switch (state) { 1017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case INIT: 1027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 1037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case APOSTROPHE: 1047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append("'"); 1057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 1067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert case PLACEHOLDER: 1077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert newPattern.append('{'); 1087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert idBuilder.appendTo(newPattern); 1097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert break; 1107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert default: 1117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalStateException(); 1127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return new SimplePatternFormatter(newPattern.toString(), placeholdersBuilder); 1147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns the max placeholder ID + 1. 1197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int getPlaceholderCount() { 1217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return placeholderCount; 1227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns true if this instance starts with placeholder with given id. 1267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean startsWithPlaceholder(int id) { 1287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (placeholderIdsOrderedByOffset.length == 0) { 1297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return false; 1307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return (placeholderIdsOrderedByOffset[0] == 0 && placeholderIdsOrderedByOffset[1] == id); 1327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats the given values. 1367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String format(CharSequence... values) { 1387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return format(new StringBuilder(), null, values).toString(); 1397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 1417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 1427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats the given values. 1437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * 1447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param appendTo the result appended here. Optimization: If the pattern this object 1457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * represents starts with a placeholder AND appendTo references the value of that same 1467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * placeholder (corresponding values parameter must also be a StringBuilder), then that 1477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * placeholder value is not copied to appendTo (Its already there). If the value of the 1487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * starting placeholder is very large, this optimization can offer huge savings. 1497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param offsets position of first value in appendTo stored in offsets[0]; 1507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * second in offsets[1]; third in offsets[2] etc. An offset of -1 means that the 1517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * corresponding value is not in appendTo. offsets.length and values.length may 1527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * differ. If caller is not interested in offsets, caller may pass null here. 1537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @param values the values 1547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * @return appendTo 1557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 1567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public StringBuilder format( 1577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert StringBuilder appendTo, int[] offsets, CharSequence... values) { 1587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (values.length < placeholderCount) { 1597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert throw new IllegalArgumentException("Too few values."); 1607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int offsetLen = offsets == null ? 0 : offsets.length; 1627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0; i < offsetLen; i++) { 1637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsets[i] = -1; 1647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (placeholderIdsOrderedByOffset.length == 0) { 1667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.append(patternWithoutPlaceholders); 1677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return appendTo; 1687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (placeholderIdsOrderedByOffset[0] > 0 || 1707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo != values[placeholderIdsOrderedByOffset[1]]) { 1717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.append( 1727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert patternWithoutPlaceholders, 1737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 0, 1747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset[0]); 1757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setPlaceholderOffset( 1767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset[1], 1777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.length(), 1787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsets, 1797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsetLen); 1807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.append(values[placeholderIdsOrderedByOffset[1]]); 1817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } else { 1827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setPlaceholderOffset( 1837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset[1], 1847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 0, 1857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsets, 1867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsetLen); 1877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 1887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 2; i < placeholderIdsOrderedByOffset.length; i += 2) { 1897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.append( 1907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert patternWithoutPlaceholders, 1917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset[i - 2], 1927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset[i]); 1937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert setPlaceholderOffset( 1947935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset[i + 1], 1957935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.length(), 1967935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsets, 1977935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsetLen); 1987935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.append(values[placeholderIdsOrderedByOffset[i + 1]]); 1997935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2007935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.append( 2017935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert patternWithoutPlaceholders, 2027935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset[placeholderIdsOrderedByOffset.length - 2], 2037935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert patternWithoutPlaceholders.length()); 2047935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return appendTo; 2057935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2067935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2077935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2087935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Formats this object using values {0}, {1} etc. Note that this is 2097935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * not the same as the original pattern string used to build this object. 2107935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2117935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert @Override 2127935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String toString() { 2137935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert String[] values = new String[this.getPlaceholderCount()]; 2147935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0; i < values.length; i++) { 2157935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert values[i] = String.format("{%d}", i); 2167935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2177935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return format(new StringBuilder(), null, values).toString(); 2187935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2197935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2207935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static void setPlaceholderOffset( 2217935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int placeholderId, int offset, int[] offsets, int offsetLen) { 2227935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (placeholderId < offsetLen) { 2237935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert offsets[placeholderId] = offset; 2247935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2257935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2267935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2277935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static enum State { 2287935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert INIT, 2297935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert APOSTROPHE, 2307935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert PLACEHOLDER, 2317935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2327935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2337935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static class PlaceholderIdBuilder { 2347935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int id = 0; 2357935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int idLen = 0; 2367935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2377935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void reset() { 2387935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert id = 0; 2397935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert idLen = 0; 2407935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2417935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2427935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int getId() { 2437935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return id; 2447935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2457935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2467935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void appendTo(StringBuilder appendTo) { 2477935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (idLen > 0) { 2487935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert appendTo.append(id); 2497935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2507935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2517935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2527935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public boolean isValid() { 2537935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return idLen > 0; 2547935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2557935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2567935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void add(char ch) { 2577935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert id = id * 10 + ch - '0'; 2587935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert idLen++; 2597935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2607935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2617935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2627935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private static class PlaceholdersBuilder { 2637935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private List<Integer> placeholderIdsOrderedByOffset = new ArrayList<Integer>(); 2647935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert private int placeholderCount = 0; 2657935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2667935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public void add(int placeholderId, int offset) { 2677935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset.add(offset); 2687935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderIdsOrderedByOffset.add(placeholderId); 2697935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert if (placeholderId >= placeholderCount) { 2707935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert placeholderCount = placeholderId + 1; 2717935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2727935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2737935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2747935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int getPlaceholderCount() { 2757935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return placeholderCount; 2767935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2777935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2787935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public int[] getPlaceholderIdsOrderedByOffset() { 2797935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert int[] result = new int[placeholderIdsOrderedByOffset.size()]; 2807935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert for (int i = 0; i < result.length; i++) { 2817935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert result[i] = placeholderIdsOrderedByOffset.get(i).intValue(); 2827935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2837935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return result; 2847935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2857935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2867935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert 2877935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert /** 2887935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert * Returns this pattern with none of the placeholders. 2897935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert */ 2907935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert public String getPatternWithNoPlaceholders() { 2917935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert return patternWithoutPlaceholders; 2927935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert } 2937935b1839a081ed19ae0d33029ad3c09632a2caaFredrik Roubert} 294