blob: d9fdf59e0ec833d34bb12f9ed30bdefdd476bd4b [file] [log] [blame]
/*
*******************************************************************************
* Copyright (C) 2011, International Business Machines
* Corporation and others. All Rights Reserved.
*******************************************************************************
* created on: 2011jul14
* created by: Markus W. Scherer
*/
package com.ibm.icu.samples.text.messagepattern;
import java.util.ArrayList;
import java.util.List;
import com.ibm.icu.text.MessagePattern;
import com.ibm.icu.text.MessagePatternUtil;
import com.ibm.icu.text.MessagePatternUtil.VariantNode;
/**
* Demo code for MessagePattern class.
* @author Markus Scherer
* @since 2011-jul-14
*/
public class MessagePatternUtilDemo {
private static final String manySpaces=" ";
private static final void printMessage(MessagePatternUtil.MessageNode msg, int depth) {
String indent = manySpaces.substring(0, depth * 2);
for (MessagePatternUtil.MessageContentsNode contents : msg.getContents()) {
switch (contents.getType()) {
case TEXT:
System.out.println(indent + "text: «" +
((MessagePatternUtil.TextNode)contents).getText() + "»");
break;
case ARG:
printArg((MessagePatternUtil.ArgNode)contents, depth);
break;
case REPLACE_NUMBER:
System.out.println(indent + "replace: number");
break;
}
}
}
private static final void printArg(MessagePatternUtil.ArgNode arg, int depth) {
System.out.print(manySpaces.substring(0, depth * 2) + "arg: «" + arg.getName() + "»");
MessagePattern.ArgType argType = arg.getArgType();
if (argType == MessagePattern.ArgType.NONE) {
System.out.println(" (no type)");
} else {
System.out.print(" (" + arg.getTypeName() + ")");
if (argType == MessagePattern.ArgType.SIMPLE) {
String styleString = arg.getSimpleStyle();
if (styleString == null) {
System.out.println(" (no style)");
} else {
System.out.println(" style: «" + styleString + "»");
}
} else {
System.out.println();
printComplexArgStyle(arg.getComplexStyle(), depth + 1);
}
}
}
private static final void printComplexArgStyle(MessagePatternUtil.ComplexArgStyleNode style,
int depth) {
if (style.hasExplicitOffset()) {
System.out.println(manySpaces.substring(0, depth * 2) + "offset: " + style.getOffset());
}
String indent = manySpaces.substring(0, depth * 2);
MessagePattern.ArgType argType = style.getArgType();
for (MessagePatternUtil.VariantNode variant : style.getVariants()) {
double value;
switch (argType) {
case CHOICE:
System.out.println(indent + variant.getSelectorValue() + " " +
variant.getSelector() + ":");
break;
case PLURAL:
value = variant.getSelectorValue();
if (value == MessagePattern.NO_NUMERIC_VALUE) {
System.out.println(indent + variant.getSelector() + ":");
} else {
System.out.println(indent + variant.getSelector() + " (" + value + "):");
}
break;
case SELECT:
System.out.println(indent + variant.getSelector() + ":");
break;
}
printMessage(variant.getMessage(), depth + 1);
}
}
/**
* This is a <em>prototype/demo/sample</em> for how we could use the MessagePatternUtil class
* for generating something like JavaScript code for evaluating some
* of the MessageFormat syntax.
*
* <p>This is not intended to be production code, nor to generate production code
* or even syntactically correct JavaScript.
* @param msg
*/
private static final void genCode(MessagePatternUtil.MessageNode msg) {
List<String> args = new ArrayList<String>();
addArgs(msg, args);
System.out.print("def function(");
boolean firstArg = true;
for (String argName : args) {
if (firstArg) {
System.out.print(argName);
firstArg = false;
} else {
System.out.print(", " + argName);
}
}
System.out.println(") {");
genCode(msg, 1, true, "");
System.out.println(" return result");
System.out.println("}");
}
private static final void genCode(MessagePatternUtil.MessageNode msg,
int depth,
boolean firstResult,
String pluralNumber) {
String prefix = manySpaces.substring(0, depth * 2) + "result ";
for (MessagePatternUtil.MessageContentsNode contents : msg.getContents()) {
String operator = firstResult ? "=" : "+=";
switch (contents.getType()) {
case TEXT:
System.out.println(
prefix + operator + " \"" +
escapeString(((MessagePatternUtil.TextNode)contents).getText()) +
"\"");
break;
case ARG:
genCode((MessagePatternUtil.ArgNode)contents, depth, firstResult);
break;
case REPLACE_NUMBER:
System.out.println(prefix + operator + " formatNumber(" + pluralNumber + ")");
break;
}
firstResult = false;
}
}
private static final void genCode(MessagePatternUtil.ArgNode arg,
int depth,
boolean firstResult) {
String prefix = manySpaces.substring(0, depth * 2) + "result ";
String operator = firstResult ? "=" : "+=";
String argName = arg.getName();
if (arg.getNumber() >= 0) {
argName = "arg_" + argName; // Prefix for numbered argument.
}
switch (arg.getArgType()) {
case NONE:
System.out.println(prefix + operator + " " + argName);
break;
case SIMPLE:
case CHOICE:
System.out.println(prefix + operator + " \"(unsupported syntax)\"");
break;
case PLURAL:
genCodeForPlural(arg.getComplexStyle(), depth, firstResult, argName);
break;
case SELECT:
genCodeForSelect(arg.getComplexStyle(), depth, firstResult, argName);
break;
}
}
private static final void genCodeForPlural(MessagePatternUtil.ComplexArgStyleNode style,
int depth,
boolean firstResult,
String argName) {
List<MessagePatternUtil.VariantNode> numericVariants =
new ArrayList<MessagePatternUtil.VariantNode>();
List<MessagePatternUtil.VariantNode> keywordVariants =
new ArrayList<MessagePatternUtil.VariantNode>();
MessagePatternUtil.VariantNode otherVariant =
style.getVariantsByType(numericVariants, keywordVariants);
double offset = style.getOffset();
String pluralNumber = offset == 0. ? argName : argName + " - " + offset;
int origDepth = depth;
if (!numericVariants.isEmpty()) {
genCodeForNumericVariants(numericVariants, depth++, firstResult, argName, pluralNumber);
}
if (!keywordVariants.isEmpty()) {
System.out.println(manySpaces.substring(0, depth * 2) +
"_keyword = PluralRules.select(" + pluralNumber + ")");
genCodeForKeywordVariants(keywordVariants, depth++, firstResult,
"_keyword", pluralNumber);
}
genCode(otherVariant.getMessage(), depth, firstResult, pluralNumber);
if (origDepth < depth) {
System.out.println(manySpaces.substring(0, --depth * 2) + "}");
if (origDepth < depth) {
System.out.println(manySpaces.substring(0, --depth * 2) + "}");
}
}
}
private static final void genCodeForSelect(MessagePatternUtil.ComplexArgStyleNode style,
int depth,
boolean firstResult,
String argName) {
List<MessagePatternUtil.VariantNode> keywordVariants =
new ArrayList<MessagePatternUtil.VariantNode>();
MessagePatternUtil.VariantNode otherVariant = style.getVariantsByType(null, keywordVariants);
if (keywordVariants.isEmpty()) {
genCode(otherVariant.getMessage(), depth, firstResult, "");
} else {
genCodeForKeywordVariants(keywordVariants, depth, firstResult, argName, "");
genCode(otherVariant.getMessage(), depth + 1, firstResult, "");
System.out.println(manySpaces.substring(0, depth * 2) + "}");
}
}
private static final void genCodeForNumericVariants(List<VariantNode> variants,
int depth,
boolean firstResult,
String varName,
String pluralNumber) {
String indent = manySpaces.substring(0, depth++ * 2);
boolean firstVariant = true;
for (MessagePatternUtil.VariantNode variant : variants) {
System.out.println(
indent +
(firstVariant ? "if (" : "} else if (") +
varName + " == " + variant.getSelectorValue() + ") {");
genCode(variant.getMessage(), depth, firstResult, pluralNumber);
firstVariant = false;
}
System.out.println(indent + "} else {");
}
private static final void genCodeForKeywordVariants(List<VariantNode> variants,
int depth,
boolean firstResult,
String varName,
String pluralNumber) {
String indent = manySpaces.substring(0, depth++ * 2);
boolean firstVariant = true;
for (MessagePatternUtil.VariantNode variant : variants) {
System.out.println(
indent +
(firstVariant ? "if (" : "} else if (") +
varName + " == \"" + variant.getSelector() + "\") {");
genCode(variant.getMessage(), depth, firstResult, pluralNumber);
firstVariant = false;
}
System.out.println(indent + "} else {");
}
/**
* Adds the message's argument names to the args list.
* Adds each argument only once, in the order of first appearance.
* Numbered arguments get an "arg_" prefix prepended.
* @param msg
* @param args
*/
private static final void addArgs(MessagePatternUtil.MessageNode msg, List<String> args) {
for (MessagePatternUtil.MessageContentsNode contents : msg.getContents()) {
if (contents.getType() == MessagePatternUtil.MessageContentsNode.Type.ARG) {
MessagePatternUtil.ArgNode arg = (MessagePatternUtil.ArgNode)contents;
String argName;
if (arg.getNumber() >= 0) {
argName = "arg_" + arg.getNumber(); // Prefix for numbered argument.
} else {
argName = arg.getName();
}
if (!args.contains(argName)) {
args.add(argName);
}
MessagePatternUtil.ComplexArgStyleNode complexStyle = arg.getComplexStyle();
if (complexStyle != null) {
for (MessagePatternUtil.VariantNode variant : complexStyle.getVariants()) {
addArgs(variant.getMessage(), args);
}
}
}
}
}
private static final String escapeString(String s) {
if (s.indexOf('"') < 0) {
return s;
} else {
return s.replace("\"", "\\\"");
}
}
private static final MessagePatternUtil.MessageNode print(String s) {
System.out.println("message: «" + s + "»");
try {
MessagePatternUtil.MessageNode msg = MessagePatternUtil.buildMessageNode(s);
printMessage(msg, 1);
genCode(msg);
return msg;
} catch(Exception e) {
System.out.println("Exception: "+e.getMessage());
return null;
}
}
public static void main(String[] argv) {
print("Hello!");
print("Hel'lo!");
print("Hel'{o");
print("Hel'{'o");
// double apostrophe inside quoted literal text still encodes a single apostrophe
print("a'{bc''de'f");
print("a'{bc''de'f{0,number,g'hi''jk'l#}");
print("abc{0}def");
print("abc{ arg }def");
print("abc{1}def{arg}ghi");
print("abc{2, number}ghi{3, select, xx {xxx} other {ooo}} xyz");
print("abc{gender,select,"+
"other{His name is {tc,XMB,<ph name=\"PERSON\">{$PERSON}</ph>}.}}xyz");
print("abc{num_people, plural, offset:17 few{fff} other {oooo}}xyz");
print("abc{ num , plural , offset: 2 =1 {1} =-1 {-1} =3.14 {3.14} other {oo} }xyz");
print("I don't {a,plural,other{w'{'on't #'#'}} and "+
"{b,select,other{shan't'}'}} '{'''know'''}' and "+
"{c,choice,0#can't'|'}"+
"{z,number,#'#'###.00'}'}.");
print("a_{0,choice,-∞ #-inf| 5≤ five | 99 # ninety'|'nine }_z");
print("a_{0,plural,other{num=#'#'=#'#'={1,number,##}!}}_z");
print("}}}{0}}"); // yes, unmatched '}' are ok in ICU MessageFormat
print("Hello {0}!");
String msg="++{0, select, female{{1} calls you her friend}"+
"other{{1} calls you '{their}' friend}"+
"male{{1} calls you his friend}}--";
print(msg);
msg="_'__{gender, select, female{Her n'ame is {person_name}.}"+
"other{His n'ame is {person_name}.}}__'_";
print(msg);
print("{num,plural,offset:1 " +
"=0{no one} =1{one, that is one and # others} " +
"one{one and # (probably 1) others} few{one and # others} " +
"other{lots & lots}}");
print(
"{p1_gender,select," +
"female{" +
"{p2_gender,select," +
"female{" +
"{num_people,plural,offset:1 "+
"=0{she alone}" +
"=1{she and her girlfriend {p2}}" +
"=2{she and her girlfriend {p2} and another}" +
"other{she, her girlfriend {p2} and # others}}}" +
"male{" +
"{num_people,plural,offset:1 "+
"=0{she alone}" +
"=1{she and her boyfriend {p2}}" +
"=2{she and her boyfriend {p2} and another}" +
"other{she, her boyfriend {p2} and # others}}}" +
"other{" +
"{num_people,plural,offset:1 "+
"=0{she alone}" +
"=1{she and her friend {p2}}" +
"=2{she and her friend {p2} and another}" +
"other{she, her friend {p2} and # others}}}}}" +
"male{" +
"{p2_gender,select," +
"female{" +
"{num_people,plural,offset:1 "+
"=0{he alone}" +
"=1{he and his girlfriend {p2}}" +
"=2{he and his girlfriend {p2} and another}" +
"other{he, his girlfriend {p2} and # others}}}" +
"male{" +
"{num_people,plural,offset:1 "+
"=0{he alone}" +
"=1{he and his boyfriend {p2}}" +
"=2{he and his boyfriend {p2} and another}" +
"other{he, his boyfriend {p2} and # others}}}" +
"other{" +
"{num_people,plural,offset:1 "+
"=0{she alone}" +
"=1{she and his friend {p2}}" +
"=2{she and his friend {p2} and another}" +
"other{she, his friend {p2} and # others}}}}}" +
"other{" +
"{p2_gender,select," +
"female{" +
"{num_people,plural,offset:1 "+
"=0{they alone}" +
"=1{they and their girlfriend {p2}}" +
"=2{they and their girlfriend {p2} and another}" +
"other{they, their girlfriend {p2} and # others}}}" +
"male{" +
"{num_people,plural,offset:1 "+
"=0{they alone}" +
"=1{they and their boyfriend {p2}}" +
"=2{they and their boyfriend {p2} and another}" +
"other{they, their boyfriend {p2} and # others}}}" +
"other{" +
"{num_people,plural,offset:1 "+
"=0{they alone}" +
"=1{they and their friend {p2}}" +
"=2{they and their friend {p2} and another}" +
"other{they, their friend {p2} and # others}}}}}}");
}
}