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