2 *******************************************************************************
3 * Copyright (C) 2011-2012, International Business Machines
4 * Corporation and others. All Rights Reserved.
5 *******************************************************************************
6 * created on: 2011aug12
7 * created by: Markus W. Scherer
10 package com.ibm.icu.dev.test.format;
12 import java.util.ArrayList;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Locale;
17 import com.ibm.icu.text.MessagePattern;
18 import com.ibm.icu.text.MessagePatternUtil;
19 import com.ibm.icu.text.MessagePatternUtil.ArgNode;
20 import com.ibm.icu.text.MessagePatternUtil.ComplexArgStyleNode;
21 import com.ibm.icu.text.MessagePatternUtil.MessageContentsNode;
22 import com.ibm.icu.text.MessagePatternUtil.MessageNode;
23 import com.ibm.icu.text.MessagePatternUtil.TextNode;
24 import com.ibm.icu.text.MessagePatternUtil.VariantNode;
27 * Test MessagePatternUtil (MessagePattern-as-tree-of-nodes API)
28 * by building parallel trees of nodes and verifying that they match.
30 public final class MessagePatternUtilTest extends com.ibm.icu.dev.test.TestFmwk {
31 public static void main(String[] args) throws Exception {
32 new MessagePatternUtilTest().run(args);
35 // The following nested "Expect..." classes are used to build
36 // a tree structure parallel to what the MessagePatternUtil class builds.
37 // These nested test classes are not static so that they have access to TestFmwk methods.
39 private class ExpectMessageNode {
40 private ExpectMessageNode expectTextThatContains(String s) {
41 contents.add(new ExpectTextNode(s));
44 private ExpectMessageNode expectReplaceNumber() {
45 contents.add(new ExpectMessageContentsNode());
48 private ExpectMessageNode expectNoneArg(Object name) {
49 contents.add(new ExpectArgNode(name));
52 private ExpectMessageNode expectSimpleArg(Object name, String type) {
53 contents.add(new ExpectArgNode(name, type));
56 private ExpectMessageNode expectSimpleArg(Object name, String type, String style) {
57 contents.add(new ExpectArgNode(name, type, style));
60 private ExpectComplexArgNode expectChoiceArg(Object name) {
61 return expectComplexArg(name, MessagePattern.ArgType.CHOICE);
63 private ExpectComplexArgNode expectPluralArg(Object name) {
64 return expectComplexArg(name, MessagePattern.ArgType.PLURAL);
66 private ExpectComplexArgNode expectSelectArg(Object name) {
67 return expectComplexArg(name, MessagePattern.ArgType.SELECT);
69 private ExpectComplexArgNode expectSelectOrdinalArg(Object name) {
70 return expectComplexArg(name, MessagePattern.ArgType.SELECTORDINAL);
72 private ExpectComplexArgNode expectComplexArg(Object name, MessagePattern.ArgType argType) {
73 ExpectComplexArgNode complexArg = new ExpectComplexArgNode(this, name, argType);
74 contents.add(complexArg);
77 private ExpectComplexArgNode finishVariant() {
80 private void checkMatches(MessageNode msg) {
81 // matches() prints all errors.
84 private boolean matches(MessageNode msg) {
85 List<MessageContentsNode> msgContents = msg.getContents();
86 boolean ok = assertEquals("different numbers of MessageContentsNode",
87 contents.size(), msgContents.size());
89 Iterator<MessageContentsNode> msgIter = msgContents.iterator();
90 for (ExpectMessageContentsNode ec : contents) {
91 ok &= ec.matches(msgIter.next());
95 errln("error in message: " + msg.toString());
99 private ExpectComplexArgNode parent; // for finishVariant()
100 private List<ExpectMessageContentsNode> contents =
101 new ArrayList<ExpectMessageContentsNode>();
105 * Base class for message contents nodes.
106 * Used directly for REPLACE_NUMBER nodes, subclassed for others.
108 private class ExpectMessageContentsNode {
109 protected boolean matches(MessageContentsNode c) {
110 return assertEquals("not a REPLACE_NUMBER node",
111 MessageContentsNode.Type.REPLACE_NUMBER, c.getType());
115 private class ExpectTextNode extends ExpectMessageContentsNode {
116 private ExpectTextNode(String subString) {
117 this.subString = subString;
120 protected boolean matches(MessageContentsNode c) {
122 assertEquals("not a TextNode",
123 MessageContentsNode.Type.TEXT, c.getType()) &&
124 assertTrue("TextNode does not contain \"" + subString + "\"",
125 ((TextNode)c).getText().contains(subString));
127 private String subString;
130 private class ExpectArgNode extends ExpectMessageContentsNode {
131 private ExpectArgNode(Object name) {
132 this(name, null, null);
134 private ExpectArgNode(Object name, String type) {
135 this(name, type, null);
137 private ExpectArgNode(Object name, String type, String style) {
138 if (name instanceof String) {
139 this.name = (String)name;
142 this.number = (Integer)name;
143 this.name = Integer.toString(this.number);
146 argType = MessagePattern.ArgType.NONE;
148 argType = MessagePattern.ArgType.SIMPLE;
154 protected boolean matches(MessageContentsNode c) {
156 assertEquals("not an ArgNode",
157 MessageContentsNode.Type.ARG, c.getType());
161 ArgNode arg = (ArgNode)c;
162 ok &= assertEquals("unexpected ArgNode argType",
163 argType, arg.getArgType());
164 ok &= assertEquals("unexpected ArgNode arg name",
165 name, arg.getName());
166 ok &= assertEquals("unexpected ArgNode arg number",
167 number, arg.getNumber());
168 ok &= assertEquals("unexpected ArgNode arg type name",
169 type, arg.getTypeName());
170 ok &= assertEquals("unexpected ArgNode arg style",
171 style, arg.getSimpleStyle());
172 if (argType == MessagePattern.ArgType.NONE || argType == MessagePattern.ArgType.SIMPLE) {
173 ok &= assertNull("unexpected non-null complex style", arg.getComplexStyle());
179 protected MessagePattern.ArgType argType;
181 private String style;
184 private class ExpectComplexArgNode extends ExpectArgNode {
185 private ExpectComplexArgNode(ExpectMessageNode parent,
186 Object name, MessagePattern.ArgType argType) {
187 super(name, argType.toString().toLowerCase(Locale.ENGLISH));
188 this.argType = argType;
189 this.parent = parent;
191 private ExpectComplexArgNode expectOffset(double offset) {
192 this.offset = offset;
193 explicitOffset = true;
196 private ExpectMessageNode expectVariant(String selector) {
197 ExpectVariantNode variant = new ExpectVariantNode(this, selector);
198 variants.add(variant);
201 private ExpectMessageNode expectVariant(String selector, double value) {
202 ExpectVariantNode variant = new ExpectVariantNode(this, selector, value);
203 variants.add(variant);
206 private ExpectMessageNode finishComplexArg() {
210 protected boolean matches(MessageContentsNode c) {
211 boolean ok = super.matches(c);
215 ArgNode arg = (ArgNode)c;
216 ComplexArgStyleNode complexStyle = arg.getComplexStyle();
217 ok &= assertNotNull("unexpected null complex style", complexStyle);
221 ok &= assertEquals("unexpected complex-style argType",
222 argType, complexStyle.getArgType());
223 ok &= assertEquals("unexpected complex-style hasExplicitOffset()",
224 explicitOffset, complexStyle.hasExplicitOffset());
225 ok &= assertEquals("unexpected complex-style offset",
226 offset, complexStyle.getOffset());
227 List<VariantNode> complexVariants = complexStyle.getVariants();
228 ok &= assertEquals("different number of variants",
229 variants.size(), complexVariants.size());
233 Iterator<VariantNode> complexIter = complexVariants.iterator();
234 for (ExpectVariantNode variant : variants) {
235 ok &= variant.matches(complexIter.next());
239 private ExpectMessageNode parent; // for finishComplexArg()
240 private boolean explicitOffset;
241 private double offset;
242 private List<ExpectVariantNode> variants = new ArrayList<ExpectVariantNode>();
245 private class ExpectVariantNode {
246 private ExpectVariantNode(ExpectComplexArgNode parent, String selector) {
247 this(parent, selector, MessagePattern.NO_NUMERIC_VALUE);
249 private ExpectVariantNode(ExpectComplexArgNode parent, String selector, double value) {
250 this.selector = selector;
251 numericValue = value;
252 msg = new ExpectMessageNode();
255 private boolean matches(VariantNode v) {
256 boolean ok = assertEquals("different selector strings",
257 selector, v.getSelector());
258 ok &= assertEquals("different selector strings",
259 isSelectorNumeric(), v.isSelectorNumeric());
260 ok &= assertEquals("different selector strings",
261 numericValue, v.getSelectorValue());
262 return ok & msg.matches(v.getMessage());
264 private boolean isSelectorNumeric() {
265 return numericValue != MessagePattern.NO_NUMERIC_VALUE;
267 private String selector;
268 private double numericValue;
269 private ExpectMessageNode msg;
272 // The actual tests start here. ---------------------------------------- ***
273 // Sample message strings are mostly from the MessagePatternUtilDemo.
275 public void TestHello() {
277 MessageNode msg = MessagePatternUtil.buildMessageNode("Hello!");
278 ExpectMessageNode expect = new ExpectMessageNode().expectTextThatContains("Hello");
279 expect.checkMatches(msg);
282 public void TestHelloWithApos() {
283 // Literal ASCII apostrophe.
284 MessageNode msg = MessagePatternUtil.buildMessageNode("Hel'lo!");
285 ExpectMessageNode expect = new ExpectMessageNode().expectTextThatContains("Hel'lo");
286 expect.checkMatches(msg);
289 public void TestHelloWithQuote() {
290 // Apostrophe starts quoted literal text.
291 MessageNode msg = MessagePatternUtil.buildMessageNode("Hel'{o!");
292 ExpectMessageNode expect = new ExpectMessageNode().expectTextThatContains("Hel{o");
293 expect.checkMatches(msg);
294 // Terminating the quote should yield the same result.
295 msg = MessagePatternUtil.buildMessageNode("Hel'{'o!");
296 expect.checkMatches(msg);
297 // Double apostrophe inside quoted literal text still encodes a single apostrophe.
298 msg = MessagePatternUtil.buildMessageNode("a'{bc''de'f");
299 expect = new ExpectMessageNode().expectTextThatContains("a{bc'def");
300 expect.checkMatches(msg);
303 public void TestNoneArg() {
304 // Numbered argument.
305 MessageNode msg = MessagePatternUtil.buildMessageNode("abc{0}def");
306 ExpectMessageNode expect = new ExpectMessageNode().
307 expectTextThatContains("abc").expectNoneArg(0).expectTextThatContains("def");
308 expect.checkMatches(msg);
310 msg = MessagePatternUtil.buildMessageNode("abc{ arg }def");
311 expect = new ExpectMessageNode().
312 expectTextThatContains("abc").expectNoneArg("arg").expectTextThatContains("def");
313 expect.checkMatches(msg);
314 // Numbered and named arguments.
315 msg = MessagePatternUtil.buildMessageNode("abc{1}def{arg}ghi");
316 expect = new ExpectMessageNode().
317 expectTextThatContains("abc").expectNoneArg(1).expectTextThatContains("def").
318 expectNoneArg("arg").expectTextThatContains("ghi");
319 expect.checkMatches(msg);
322 public void TestSimpleArg() {
323 MessageNode msg = MessagePatternUtil.buildMessageNode("a'{bc''de'f{0,number,g'hi''jk'l#}");
324 ExpectMessageNode expect = new ExpectMessageNode().
325 expectTextThatContains("a{bc'def").expectSimpleArg(0, "number", "g'hi''jk'l#");
326 expect.checkMatches(msg);
329 public void TestSelectArg() {
330 MessageNode msg = MessagePatternUtil.buildMessageNode(
331 "abc{2, number}ghi{3, select, xx {xxx} other {ooo}} xyz");
332 ExpectMessageNode expect = new ExpectMessageNode().
333 expectTextThatContains("abc").expectSimpleArg(2, "number").
334 expectTextThatContains("ghi").
336 expectVariant("xx").expectTextThatContains("xxx").finishVariant().
337 expectVariant("other").expectTextThatContains("ooo").finishVariant().
339 expectTextThatContains(" xyz");
340 expect.checkMatches(msg);
343 public void TestPluralArg() {
344 // Plural with only keywords.
345 MessageNode msg = MessagePatternUtil.buildMessageNode(
346 "abc{num_people, plural, offset:17 few{fff} other {oooo}}xyz");
347 ExpectMessageNode expect = new ExpectMessageNode().
348 expectTextThatContains("abc").
349 expectPluralArg("num_people").
351 expectVariant("few").expectTextThatContains("fff").finishVariant().
352 expectVariant("other").expectTextThatContains("oooo").finishVariant().
354 expectTextThatContains("xyz");
355 expect.checkMatches(msg);
356 // Plural with explicit-value selectors.
357 msg = MessagePatternUtil.buildMessageNode(
358 "abc{ num , plural , offset: 2 =1 {1} =-1 {-1} =3.14 {3.14} other {oo} }xyz");
359 expect = new ExpectMessageNode().
360 expectTextThatContains("abc").
361 expectPluralArg("num").
363 expectVariant("=1", 1).expectTextThatContains("1").finishVariant().
364 expectVariant("=-1", -1).expectTextThatContains("-1").finishVariant().
365 expectVariant("=3.14", 3.14).expectTextThatContains("3.14").finishVariant().
366 expectVariant("other").expectTextThatContains("oo").finishVariant().
368 expectTextThatContains("xyz");
369 expect.checkMatches(msg);
370 // Plural with number replacement.
371 msg = MessagePatternUtil.buildMessageNode(
372 "a_{0,plural,other{num=#'#'=#'#'={1,number,##}!}}_z");
373 expect = new ExpectMessageNode().
374 expectTextThatContains("a_").
376 expectVariant("other").
377 expectTextThatContains("num=").expectReplaceNumber().
378 expectTextThatContains("#=").expectReplaceNumber().
379 expectTextThatContains("#=").expectSimpleArg(1, "number", "##").
380 expectTextThatContains("!").finishVariant().
382 expectTextThatContains("_z");
383 expect.checkMatches(msg);
384 // Plural with explicit offset:0.
385 msg = MessagePatternUtil.buildMessageNode(
386 "a_{0,plural,offset:0 other{num=#!}}_z");
387 expect = new ExpectMessageNode().
388 expectTextThatContains("a_").
391 expectVariant("other").
392 expectTextThatContains("num=").expectReplaceNumber().
393 expectTextThatContains("!").finishVariant().
395 expectTextThatContains("_z");
396 expect.checkMatches(msg);
400 public void TestSelectOrdinalArg() {
401 MessageNode msg = MessagePatternUtil.buildMessageNode(
402 "abc{num, selectordinal, offset:17 =0{null} few{fff} other {oooo}}xyz");
403 ExpectMessageNode expect = new ExpectMessageNode().
404 expectTextThatContains("abc").
405 expectSelectOrdinalArg("num").
407 expectVariant("=0", 0).expectTextThatContains("null").finishVariant().
408 expectVariant("few").expectTextThatContains("fff").finishVariant().
409 expectVariant("other").expectTextThatContains("oooo").finishVariant().
411 expectTextThatContains("xyz");
412 expect.checkMatches(msg);
415 public void TestChoiceArg() {
416 MessageNode msg = MessagePatternUtil.buildMessageNode(
417 "a_{0,choice,-∞ #-inf| 5≤ five | 99 # ninety'|'nine }_z");
418 ExpectMessageNode expect = new ExpectMessageNode().
419 expectTextThatContains("a_").
421 expectVariant("#", Double.NEGATIVE_INFINITY).
422 expectTextThatContains("-inf").finishVariant().
423 expectVariant("≤", 5).expectTextThatContains(" five ").finishVariant().
424 expectVariant("#", 99).expectTextThatContains(" ninety|nine ").finishVariant().
426 expectTextThatContains("_z");
427 expect.checkMatches(msg);
430 public void TestComplexArgs() {
431 MessageNode msg = MessagePatternUtil.buildMessageNode(
432 "I don't {a,plural,other{w'{'on't #'#'}} and "+
433 "{b,select,other{shan't'}'}} '{'''know'''}' and "+
434 "{c,choice,0#can't'|'}"+
435 "{z,number,#'#'###.00'}'}.");
436 ExpectMessageNode expect = new ExpectMessageNode().
437 expectTextThatContains("I don't ").
438 expectPluralArg("a").
439 expectVariant("other").
440 expectTextThatContains("w{on't ").expectReplaceNumber().
441 expectTextThatContains("#").finishVariant().
443 expectTextThatContains(" and ").
444 expectSelectArg("b").
445 expectVariant("other").expectTextThatContains("shan't}").finishVariant().
447 expectTextThatContains(" {'know'} and ").
448 expectChoiceArg("c").
449 expectVariant("#", 0).expectTextThatContains("can't|").finishVariant().
451 expectSimpleArg("z", "number", "#'#'###.00'}'").
452 expectTextThatContains(".");
453 expect.checkMatches(msg);
457 * @return the text string of the VariantNode's message;
458 * assumes that its message consists of only text
460 private String variantText(VariantNode v) {
461 return ((TextNode)v.getMessage().getContents().get(0)).getText();
464 public void TestPluralVariantsByType() {
465 MessageNode msg = MessagePatternUtil.buildMessageNode(
466 "{p,plural,a{A}other{O}=4{iv}b{B}other{U}=2{ii}}");
467 ExpectMessageNode expect = new ExpectMessageNode().
468 expectPluralArg("p").
469 expectVariant("a").expectTextThatContains("A").finishVariant().
470 expectVariant("other").expectTextThatContains("O").finishVariant().
471 expectVariant("=4", 4).expectTextThatContains("iv").finishVariant().
472 expectVariant("b").expectTextThatContains("B").finishVariant().
473 expectVariant("other").expectTextThatContains("U").finishVariant().
474 expectVariant("=2", 2).expectTextThatContains("ii").finishVariant().
476 if (!expect.matches(msg)) {
479 List<VariantNode> numericVariants = new ArrayList<VariantNode>();
480 List<VariantNode> keywordVariants = new ArrayList<VariantNode>();
482 ((ArgNode)msg.getContents().get(0)).getComplexStyle().
483 getVariantsByType(numericVariants, keywordVariants);
484 assertEquals("'other' selector", "other", other.getSelector());
485 assertEquals("message string of first 'other'", "O", variantText(other));
487 assertEquals("numericVariants.size()", 2, numericVariants.size());
488 VariantNode v = numericVariants.get(0);
489 assertEquals("numericVariants[0] selector", "=4", v.getSelector());
490 assertEquals("numericVariants[0] selector value", 4., v.getSelectorValue());
491 assertEquals("numericVariants[0] text", "iv", variantText(v));
492 v = numericVariants.get(1);
493 assertEquals("numericVariants[1] selector", "=2", v.getSelector());
494 assertEquals("numericVariants[1] selector value", 2., v.getSelectorValue());
495 assertEquals("numericVariants[1] text", "ii", variantText(v));
497 assertEquals("keywordVariants.size()", 2, keywordVariants.size());
498 v = keywordVariants.get(0);
499 assertEquals("keywordVariants[0] selector", "a", v.getSelector());
500 assertFalse("keywordVariants[0].isSelectorNumeric()", v.isSelectorNumeric());
501 assertEquals("keywordVariants[0] text", "A", variantText(v));
502 v = keywordVariants.get(1);
503 assertEquals("keywordVariants[1] selector", "b", v.getSelector());
504 assertFalse("keywordVariants[1].isSelectorNumeric()", v.isSelectorNumeric());
505 assertEquals("keywordVariants[1] text", "B", variantText(v));
508 public void TestSelectVariantsByType() {
509 MessageNode msg = MessagePatternUtil.buildMessageNode(
510 "{s,select,a{A}other{O}b{B}other{U}}");
511 ExpectMessageNode expect = new ExpectMessageNode().
512 expectSelectArg("s").
513 expectVariant("a").expectTextThatContains("A").finishVariant().
514 expectVariant("other").expectTextThatContains("O").finishVariant().
515 expectVariant("b").expectTextThatContains("B").finishVariant().
516 expectVariant("other").expectTextThatContains("U").finishVariant().
518 if (!expect.matches(msg)) {
521 // Check that we can use numericVariants = null.
522 List<VariantNode> keywordVariants = new ArrayList<VariantNode>();
524 ((ArgNode)msg.getContents().get(0)).getComplexStyle().
525 getVariantsByType(null, keywordVariants);
526 assertEquals("'other' selector", "other", other.getSelector());
527 assertEquals("message string of first 'other'", "O", variantText(other));
529 assertEquals("keywordVariants.size()", 2, keywordVariants.size());
530 VariantNode v = keywordVariants.get(0);
531 assertEquals("keywordVariants[0] selector", "a", v.getSelector());
532 assertFalse("keywordVariants[0].isSelectorNumeric()", v.isSelectorNumeric());
533 assertEquals("keywordVariants[0] text", "A", variantText(v));
534 v = keywordVariants.get(1);
535 assertEquals("keywordVariants[1] selector", "b", v.getSelector());
536 assertFalse("keywordVariants[1].isSelectorNumeric()", v.isSelectorNumeric());
537 assertEquals("keywordVariants[1] text", "B", variantText(v));