]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/demos/src/com/ibm/icu/dev/demo/messagepattern/MiniMessageFormatter.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / demos / src / com / ibm / icu / dev / demo / messagepattern / MiniMessageFormatter.java
1 /*
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
8 */
9
10 package com.ibm.icu.dev.demo.messagepattern;
11
12 import java.io.IOException;
13 import java.util.HashMap;
14 import java.util.Map;
15
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;
20
21 /**
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
25  * @since 2010-aug-21
26  */
27 public final class MiniMessageFormatter implements Freezable<MiniMessageFormatter> {
28     public MiniMessageFormatter() {
29         this.msg=new MessagePattern();
30     }
31
32     public MiniMessageFormatter(MessagePattern msg) {
33         this.msg=(MessagePattern)msg.clone();
34     }
35
36     public MiniMessageFormatter(String msg) {
37         this.msg=new MessagePattern(msg);
38     }
39
40     public MiniMessageFormatter applyPattern(String msg) {
41         this.msg.parse(msg);
42         return this;
43     }
44
45     public String getPatternString() {
46         return msg.getPatternString();
47     }
48
49     public boolean hasNamedArguments() {
50         return msg.hasNamedArguments();
51     }
52
53     public boolean hasNumberedArguments() {
54         return msg.hasNumberedArguments();
55     }
56
57     /**
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
62      * @return dest
63      */
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.");
68         }
69         format(0, dest, args, null);
70         return dest;
71     }
72
73     public static final String format(String msg, Object... args) {
74         return new MiniMessageFormatter(msg).format(new StringBuilder(2*msg.length()), args).toString();
75     }
76
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.");
81         }
82         format(0, dest, null, argsMap);
83         return dest;
84     }
85
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();
88     }
89
90     private int format(int msgStart, Appendable dest, Object[] args, Map<String, Object> argsMap) {
91         try {
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) {
100                     return i;
101                 }
102                 if(type==Part.Type.SKIP_SYNTAX || type==Part.Type.INSERT_CHAR) {
103                     prevIndex=part.getLimit();
104                     continue;
105                 }
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);
110                 Object arg;
111                 if(args!=null) {
112                     try {
113                         arg=args[part.getValue()];  // args[ARG_NUMBER]
114                     } catch(IndexOutOfBoundsException e) {
115                         throw new IndexOutOfBoundsException(
116                             "No argument at index "+part.getValue());
117                     }
118                 } else {
119                     arg=argsMap.get(msg.getSubstring(part));  // args[ARG_NAME]
120                     if(arg==null) {
121                         throw new IndexOutOfBoundsException(
122                             "No argument for name "+msg.getSubstring(part));
123                     }
124                 }
125                 String argValue=arg.toString();
126                 ++i;
127                 if(argType==ArgType.NONE) {
128                     dest.append(argValue);
129                 } else if(argType==ArgType.SELECT) {
130                     // Similar to SelectFormat.findSubMessage().
131                     int subMsgStart=0;
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.
136                             break;
137                         // else: part is an ARG_SELECTOR followed by a message
138                         } else if(msg.partSubstringMatches(part, argValue)) {
139                             // keyword matches
140                             subMsgStart=i;
141                             break;
142                         } else if(subMsgStart==0 && msg.partSubstringMatches(part, "other")) {
143                             subMsgStart=i;
144                         }
145                         i=msg.getLimitPartIndex(i);
146                     }
147                     format(subMsgStart, dest, args, argsMap);
148                 } else {
149                     throw new UnsupportedOperationException("Unsupported argument type "+argType);
150                 }
151                 prevIndex=msg.getPart(argLimit).getLimit();
152                 i=argLimit;
153             }
154         } catch(IOException e) {  // Appendable throws IOException
155             throw new RuntimeException(e);  // We do not want a throws clause.
156         }
157     }
158
159     /**
160      * Presents an array of (String, Object) pairs as a Map.
161      * Only for temporary use for formatting with named arguments.
162      */
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]);
167         }
168         return argsMap;
169     }
170
171     public MiniMessageFormatter cloneAsThawed() {
172         // TODO Auto-generated method stub
173         return null;
174     }
175
176     public MiniMessageFormatter freeze() {
177         msg.freeze();
178         return this;
179     }
180
181     public boolean isFrozen() {
182         return msg.isFrozen();
183     }
184
185     private final MessagePattern msg;
186 }