12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others. 22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License 387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert/* 487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert ******************************************************************************* 587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Copyright (C) 2014-2016, International Business Machines Corporation and 687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * others. All Rights Reserved. 787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert ******************************************************************************* 887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubertpackage com.ibm.icu.text; 1087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 1187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubertimport com.ibm.icu.impl.SimpleFormatterImpl; 1287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 1387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert/** 1487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Formats simple patterns like "{1} was born in {0}". 1587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Minimal subset of MessageFormat; fast, simple, minimal dependencies. 1687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Supports only numbered arguments with no type nor style parameters, 1787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * and formats only string values. 1887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Quoting via ASCII apostrophe compatible with ICU MessageFormat default behavior. 1987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 2087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <p>Factory methods throw exceptions for syntax errors 2187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * and for too few or too many arguments/placeholders. 2287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 2387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <p>SimpleFormatter objects are immutable and can be safely cached like strings. 2487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 2587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <p>Example: 2687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * <pre> 2787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * SimpleFormatter fmt = SimpleFormatter.compile("{1} '{born}' in {0}"); 2887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 2987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * // Output: "paul {born} in england" 3087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * System.out.println(fmt.format("england", "paul")); 3187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * </pre> 3287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 3387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @see MessageFormat 3487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @see MessagePattern.ApostropheMode 3587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 3687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 3787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 3887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubertpublic final class SimpleFormatter { 3987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert // For internal use in Java, use SimpleFormatterImpl directly instead: 4087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert // It is most efficient to compile patterns to compiled-pattern strings 4187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert // and use them with static methods. 4287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert // (Avoids allocating SimpleFormatter wrapper objects.) 4387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 4487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 4587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Binary representation of the compiled pattern. 4687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @see SimpleFormatterImpl 4787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 4887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert private final String compiledPattern; 4987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 5087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert private SimpleFormatter(String compiledPattern) { 5187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert this.compiledPattern = compiledPattern; 5287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 5387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 5487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 5587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Creates a formatter from the pattern string. 5687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 5787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param pattern The pattern string. 5887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @return The new SimpleFormatter object. 5987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @throws IllegalArgumentException for bad argument syntax. 6087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 6187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 6287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 6387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public static SimpleFormatter compile(CharSequence pattern) { 6487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return compileMinMaxArguments(pattern, 0, Integer.MAX_VALUE); 6587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 6687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 6787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 6887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Creates a formatter from the pattern string. 6987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * The number of arguments checked against the given limits is the 7087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * highest argument number plus one, not the number of occurrences of arguments. 7187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 7287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param pattern The pattern string. 7387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param min The pattern must have at least this many arguments. 7487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param max The pattern must have at most this many arguments. 7587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @return The new SimpleFormatter object. 7687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @throws IllegalArgumentException for bad argument syntax and too few or too many arguments. 7787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 7887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 7987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 8087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public static SimpleFormatter compileMinMaxArguments(CharSequence pattern, int min, int max) { 8187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert StringBuilder sb = new StringBuilder(); 8287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert String compiledPattern = SimpleFormatterImpl.compileToStringMinMaxArguments(pattern, sb, min, max); 8387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return new SimpleFormatter(compiledPattern); 8487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 8587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 8687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 8787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @return The max argument number + 1. 8887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 8987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 9087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 9187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public int getArgumentLimit() { 9287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return SimpleFormatterImpl.getArgumentLimit(compiledPattern); 9387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 9487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 9587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 9687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Formats the given values. 9787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 9887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 9987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 10087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public String format(CharSequence... values) { 10187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return SimpleFormatterImpl.formatCompiledPattern(compiledPattern, values); 10287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 10387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 10487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 10587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Formats the given values, appending to the appendTo builder. 10687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 10787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param appendTo Gets the formatted pattern and values appended. 10887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param offsets offsets[i] receives the offset of where 10987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * values[i] replaced pattern argument {i}. 11087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Can be null, or can be shorter or longer than values. 11187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * If there is no {i} in the pattern, then offsets[i] is set to -1. 11287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param values The argument values. 11387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * An argument value must not be the same object as appendTo. 11487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * values.length must be at least getArgumentLimit(). 11587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Can be null if getArgumentLimit()==0. 11687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @return appendTo 11787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 11887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 11987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 12087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public StringBuilder formatAndAppend( 12187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert StringBuilder appendTo, int[] offsets, CharSequence... values) { 12287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return SimpleFormatterImpl.formatAndAppend(compiledPattern, appendTo, offsets, values); 12387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 12487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 12587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 12687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Formats the given values, replacing the contents of the result builder. 12787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * May optimize by actually appending to the result if it is the same object 12887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * as the value corresponding to the initial argument in the pattern. 12987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 13087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param result Gets its contents replaced by the formatted pattern and values. 13187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param offsets offsets[i] receives the offset of where 13287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * values[i] replaced pattern argument {i}. 13387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Can be null, or can be shorter or longer than values. 13487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * If there is no {i} in the pattern, then offsets[i] is set to -1. 13587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @param values The argument values. 13687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * An argument value may be the same object as result. 13787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * values.length must be at least getArgumentLimit(). 13887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @return result 13987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 14087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 14187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 14287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public StringBuilder formatAndReplace( 14387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert StringBuilder result, int[] offsets, CharSequence... values) { 14487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return SimpleFormatterImpl.formatAndReplace(compiledPattern, result, offsets, values); 14587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 14687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 14787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 14887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Returns a string similar to the original pattern, only for debugging. 14987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 15087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 15187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 15287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 15387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert @Override 15487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public String toString() { 15587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert String[] values = new String[getArgumentLimit()]; 15687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert for (int i = 0; i < values.length; i++) { 15787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert values[i] = "{" + i + '}'; 15887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 15987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return formatAndAppend(new StringBuilder(), null, values).toString(); 16087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 16187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert 16287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert /** 16387255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Returns the pattern text with none of the arguments. 16487255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * Like formatting with all-empty string values. 16587255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * 16687255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @draft ICU 57 16787255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert * @provisional This API might change or be removed in a future release. 16887255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert */ 16987255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert public String getTextWithNoArguments() { 17087255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert return SimpleFormatterImpl.getTextWithNoArguments(compiledPattern); 17187255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert } 17287255a3fc79cc94374b5b8adc76a86e251ac7d3eFredrik Roubert} 173