2 *******************************************************************************
3 * Copyright (C) 2010-2011, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *******************************************************************************
6 * created on: 2010aug21
7 * created by: Markus W. Scherer
10 package com.ibm.icu.dev.demo.messagepattern;
12 import java.io.IOException;
13 import java.util.HashMap;
16 import com.ibm.icu.text.MessagePattern;
17 import com.ibm.icu.text.MessagePattern.ArgType;
18 import com.ibm.icu.text.MessagePattern.Part;
19 import com.ibm.icu.util.Freezable;
22 * Mini message formatter for a small subset of the ICU MessageFormat syntax.
23 * Supports only string substitution and select formatting.
24 * @author Markus Scherer
27 public final class MiniMessageFormatter implements Freezable<MiniMessageFormatter> {
28 public MiniMessageFormatter() {
29 this.msg=new MessagePattern();
32 public MiniMessageFormatter(MessagePattern msg) {
33 this.msg=(MessagePattern)msg.clone();
36 public MiniMessageFormatter(String msg) {
37 this.msg=new MessagePattern(msg);
40 public MiniMessageFormatter applyPattern(String msg) {
45 public String getPatternString() {
46 return msg.getPatternString();
49 public boolean hasNamedArguments() {
50 return msg.hasNamedArguments();
53 public boolean hasNumberedArguments() {
54 return msg.hasNumberedArguments();
58 * Formats the parsed message with positional arguments.
59 * Supports only string substitution (e.g., {3}) and select format.
60 * @param dest gets the formatted message appended
61 * @param args positional arguments
64 public Appendable format(Appendable dest, Object... args) {
65 if(msg.hasNamedArguments()) {
66 throw new IllegalArgumentException(
67 "Formatting message with named arguments using positional argument values.");
69 format(0, dest, args, null);
73 public static final String format(String msg, Object... args) {
74 return new MiniMessageFormatter(msg).format(new StringBuilder(2*msg.length()), args).toString();
77 public Appendable format(Appendable dest, Map<String, Object> argsMap) {
78 if(msg.hasNumberedArguments()) {
79 throw new IllegalArgumentException(
80 "Formatting message with numbered arguments using named argument values.");
82 format(0, dest, null, argsMap);
86 public static final String format(String msg, Map<String, Object> argsMap) {
87 return new MiniMessageFormatter(msg).format(new StringBuilder(2*msg.length()), argsMap).toString();
90 private int format(int msgStart, Appendable dest, Object[] args, Map<String, Object> argsMap) {
92 String msgString=msg.getPatternString();
93 int prevIndex=msg.getPart(msgStart).getLimit();
94 for(int i=msgStart+1;; ++i) {
95 Part part=msg.getPart(i);
96 Part.Type type=part.getType();
97 int index=part.getIndex();
98 dest.append(msgString, prevIndex, index);
99 if(type==Part.Type.MSG_LIMIT) {
102 if(type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR) {
103 prevIndex=part.getLimit();
106 assert type==Part.Type.ARG_START : "Unexpected Part "+part+" in parsed message.";
107 int argLimit=msg.getLimitPartIndex(i);
108 ArgType argType=part.getArgType();
109 part=msg.getPart(++i);
113 arg=args[part.getValue()]; // args[ARG_NUMBER]
114 } catch(IndexOutOfBoundsException e) {
115 throw new IndexOutOfBoundsException(
116 "No argument at index "+part.getValue());
119 arg=argsMap.get(msg.getSubstring(part)); // args[ARG_NAME]
121 throw new IndexOutOfBoundsException(
122 "No argument for name "+msg.getSubstring(part));
125 String argValue=arg.toString();
127 if(argType==ArgType.NONE) {
128 dest.append(argValue);
129 } else if(argType==ArgType.SELECT) {
130 // Similar to SelectFormat.findSubMessage().
132 for(;; ++i) { // (ARG_SELECTOR, message) pairs until ARG_LIMIT
133 part=msg.getPart(i++);
134 if(part.getType()==Part.Type.ARG_LIMIT) {
135 assert subMsgStart!=0; // The parser made sure this is the case.
137 // else: part is an ARG_SELECTOR followed by a message
138 } else if(msg.partSubstringMatches(part, argValue)) {
142 } else if(subMsgStart==0 && msg.partSubstringMatches(part, "other")) {
145 i=msg.getLimitPartIndex(i);
147 format(subMsgStart, dest, args, argsMap);
149 throw new UnsupportedOperationException("Unsupported argument type "+argType);
151 prevIndex=msg.getPart(argLimit).getLimit();
154 } catch(IOException e) { // Appendable throws IOException
155 throw new RuntimeException(e); // We do not want a throws clause.
160 * Presents an array of (String, Object) pairs as a Map.
161 * Only for temporary use for formatting with named arguments.
163 public static Map<String, Object> mapFromNameValuePairs(Object[] args) {
164 HashMap<String, Object> argsMap = new HashMap<String, Object>();
165 for(int i=0; i<args.length; i+=2) {
166 argsMap.put((String)args[i], args[i+1]);
171 public MiniMessageFormatter cloneAsThawed() {
172 // TODO Auto-generated method stub
176 public MiniMessageFormatter freeze() {
181 public boolean isFrozen() {
182 return msg.isFrozen();
185 private final MessagePattern msg;