| /* |
| ******************************************************************************* |
| * 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}}}}}}"); |
| } |
| } |