/* * @(#)MessageFormatDemo.java 1.1 96/11/23 * * (C) Copyright Taligent, Inc. 1996 - All Rights Reserved * (C) Copyright IBM Corp. 1996 - All Rights Reserved * * Portions copyright (c) 1996 Sun Microsystems, Inc. All Rights Reserved. * * The original version of this source code and documentation is copyrighted * and owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These * materials are provided under terms of a License Agreement between Taligent * and Sun. This technology is protected by multiple US and International * patents. This notice and attribution to Taligent may not be removed. * Taligent is a registered trademark of Taligent, Inc. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes and without * fee is hereby granted provided that this copyright notice * appears in all copies. Please refer to the file "copyright.html" * for further important copyright and licensing information. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. * */ import java.applet.Applet; import java.awt.event.*; import java.awt.*; import java.util.*; import java.lang.*; import java.text.*; /** *

Pattern formats are used to put together sequences of strings, numbers, * dates, and other formats to create messages. The pattern formatters * facilitate localization because they prevent both hard-coding of message * strings, and hard-coding of the concatenation sequence for portions * of message strings. This means localizers can change the content, format, * and order of any text as appropriate for any language. *

*

* About the Pattern Format  * Formatting Arguments
* Supporting Multiple Choices *

*
*

About the Pattern Format

*

The pattern is a sequence of text and arguments, all of which can be * edited. The positions of the arguments within the sequence are indicated * by a "%" followed by a digit n identifying the * argument. The pattern in the demo applet has three arguments, numbered 0, * 1, and 2. The arguments can appear in any order within the sequence, which * can be edited and modified. *

* * * * * * * * * * * * *
To See This...Do This...
You can move arguments freely within the sequence * or delete arguments. You can also edit or translate any of the unformatted * text. * * * * * * *
1. * Move the string "on %2" from the end of the pattern to * the front and correct the capitalization *
*
Translations are provided in the demo applet for * the G7 countries. * * * * * * * * * * *
1. * Pull down the Locale menu *
2. * Try several different locales with the up and down arrow keys (on * Windows) or the mouse button (on Macintosh) *
*
*

Note: To add a real percentage character to the * pattern, enter "%%". *

*
*

Formatting Arguments

*

The arguments can be either simple text strings or formattable, * localizable objects. The pattern in the demo applet, for example, includes * a date, an unformatted string, and a more complex format called a choice * format (described below). You can edit these arguments at will. When * localizing, you can also select any format to be associated with an * argument. *

* * * * * * * * * * * * * *
To See This... Do This...
You can modify the value of any argument. * * * * * * *
1. * Select the "3" in argument 2 and change it to another number * —the formatted date adjusts to the new value *
*
You can change the format of any argument, and can * specify "unformatted" arguments that are not localized. * * * * * * * * * * * * * * *
1. * Change the format type for argument 2 to None *
2. * Try different locales and notice that the date does not reformat for * different locales *
3. * Return the format back to Date and try different locales again. * The date reformats. *
*
*
*

Supporting Multiple Choices

*

Choice formats, like that used for argument 0, let localizers create * more natural messages, avoiding phrases like "3 file(s)". As * shown here, the correct text can be chosen for different numbers. This * works even in more complicated contexts, such as Slavic languages which * have more than one plural format based on the number involved. A particular * choice is chosen based on the value of the argument, and each choice can be * edited individually. *

*

Look also at the format for the choice associated with values of 2 and * higher, "%0|3". The vertical bar indicates that the choice * uses the value for argument 0, but the format for argument 3 (in this case, * a Number). This allows for a degree of flexibility in using different * formats. *

* * * * * * * * * * * * * * * * * *
To See This...Do This...
You can edit the value of any of the choice * options. * * * * * * * * * * *
1. * Select the Choice String "no files" and type in "not a * single file" *
2. * Select the value for argument 0 and type in "0" *
*
You can establish different choices for parameters * based on the value of an argument, so that strings are substituted that * agree numerically. * * * * * * * * * * *
1. * Select the value for argument 0 and type in "0", then replace * it with a "1", and then with a "2". The string changes * correspondingly. *
2. * Select argument 0 and return the value to "0". Choose the * French or German locale and notice that it makes correct substitutions in * any language. *
*
You can add as many alternatives as you need for * different value ranges. * * * * * * * * * * * * * * * * * * *
1. * Select the U.S. English locale *
2. * Select the empty choice value box (under the "2") and type * in "10" *
3. * Type in "many files" in the corresponding Choice Strings * field *
4. * Enter a number larger than10 for the value of argument 0—" * many files" is substituted in the resulting message *
*
*

You can type in other text in the pattern, arguments, or choices fields * to see different formatting behaviors. Try it out! *

* @see java.util.Format * @see java.util.MessageFormat * @version 1.1 11/23/96 * @author Laura Werner, Mark Davis */ public class MessageFormatDemo extends DemoApplet { /** * The main function which defines the behavior of the MessageFormatDemo * applet when an applet is started. */ public static void main(String argv[]) { DemoApplet.showDemo(new MessageFormatFrame(null)); } /** * This creates a MessageFormatFrame for the demo applet. */ public Frame createDemoFrame(DemoApplet applet) { return new MessageFormatFrame(applet); } } /** * A Frame is a top-level window with a title. The default layout for a frame * is BorderLayout. The MessageFormatFrame class defines the window layout of * MessageFormatDemo. */ class MessageFormatFrame extends Frame implements WindowListener, ItemListener, KeyListener { /** * Constructs a new MessageFormatFrame that is initially invisible. */ public MessageFormatFrame(DemoApplet applet) { super("Message Formatting Demo"); this.applet = applet; addWindowListener(this); init(); start(); } /** * Initializes the applet. You never need to call this directly, it * is called automatically by the system once the applet is created. */ public void init() { //Get all locales for debugging, but only get G7 locales for demos. if (DEBUG == true) locales = NumberFormat.getAvailableLocales(); else locales = Utility.getG7Locales(); buildGUI(); } /** * Called to start the applet. You never need to call this method * directly, it is called when the applet's document is visited. */ public void start() { // Stick some initial data into the controls.... arg1Text.setText("3"); arg1Type.select(CHOICE); arg2Text.setText("MyDisk"); arg2Type.select(NONE); arg3Text.setText("3 Mar 96"); arg3Type.select(DATE); arg4Text.setText(""); arg4Type.select(NUMBER); patternText.setText("The disk '%1' contained %0 on %2."); // Set up the choice format controls too choice1Num.setText("0"); choice2Num.setText("1"); choice3Num.setText("2"); resetFormat(); doFormatting(); } /** * Reset to the default message format using the ResourceBundle mechanism. * @see java.util.ResourceBundle * @see java.util.ListResourceBundle */ public void resetFormat() { Locale locale = locales[localeMenu.getSelectedIndex()]; ClassLoader classLoader = this.getClass().getClassLoader(); choice4Num.setText(""); choice4Text.setText(""); ResourceBundle choiceResources = ResourceBundle.getBundle("ChoiceResource", locale); patternText.setText(choiceResources.getString("patternText")); choice1Text.setText(choiceResources.getString("choice1")); choice2Text.setText(choiceResources.getString("choice2")); choice3Text.setText(choiceResources.getString("choice3")); } /** * Create a new format based on the selected type. For example, a new * format needs to be created if a different locale or format type is * selected. */ public Format createFormat(Choice typeMenu) { int type = typeMenu.getSelectedIndex(); Locale locale = locales[localeMenu.getSelectedIndex()]; Format result = null; if (type == NUMBER) { result = NumberFormat.getInstance(locale); } else if (type == DATE) { result = DateFormat.getDateInstance(DateFormat.DEFAULT, locale); } else if (type == CHOICE) { result = choiceFormat; // XXX } return result; } /** * Create a new format based on the selection changes and update the * output text area. */ public void doFormatting() { // Create a ChoiceFormat based on the settings in the choice field double[] limits = new double[4]; limits[0] = doubleValue(choice1Num.getText()); limits[1] = doubleValue(choice2Num.getText()); limits[2] = doubleValue(choice3Num.getText()); limits[3] = doubleValue(choice4Num.getText()); String[] choices = new String[4]; choices[0] = choice1Text.getText(); choices[1] = choice2Text.getText(); choices[2] = choice3Text.getText(); choices[3] = choice4Text.getText(); choiceFormat = new ChoiceFormat(limits, choices); // XXX // Create the individual formatters for the items in the pattern.... Format[] formats = new Format[4]; formats[0] = createFormat(arg1Type); formats[1] = createFormat(arg2Type); formats[2] = createFormat(arg3Type); formats[3] = createFormat(arg4Type); // Now create the Message Formatter itself MessageFormat format = new MessageFormat(patternText.getText()); // Create the array of objects to format.... Object[] objects = new Object[4]; objects[0] = createObject(arg1Type, arg1Text); objects[1] = createObject(arg2Type, arg2Text); objects[2] = createObject(arg3Type, arg3Text); objects[3] = createObject(arg4Type, arg4Text); String result = null; try { result = format.format(objects); } catch (Exception e) { errorText("format threw an exception: " + e.toString()); result = "ERROR"; } resultText.setText(result); } /* ItemListener method */ public void itemStateChanged(ItemEvent e) { if((e.getSource() == arg1Type) || (e.getSource() == arg2Type) || (e.getSource() == arg3Type) || (e.getSource() == arg4Type)) { doFormatting(); } else if (e.getSource() == localeMenu) { resetFormat(); doFormatting(); } } /* KeyListener methods */ public void keyPressed(KeyEvent e) { } public void keyReleased(KeyEvent e) { if ((e.getSource() == choice1Text) || (e.getSource() == choice2Text) || (e.getSource() == choice3Text) || (e.getSource() == choice4Text)) { e.consume(); doFormatting(); } else if ((e.getSource() == choice1Num) ||(e.getSource() == choice2Num) || (e.getSource() == choice3Num) ||(e.getSource() == choice4Num)) { e.consume(); doFormatting(); } else if ((e.getSource() == arg1Text) ||(e.getSource() == arg2Text) || (e.getSource() == arg3Text) ||(e.getSource() == arg4Text)) { e.consume(); doFormatting(); } else if (e.getSource() == patternText) { e.consume(); doFormatting(); } } public void keyTyped(KeyEvent e) { } /* Window Listener methods */ public void windowClosed(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void windowClosing(WindowEvent e) { setVisible(false); dispose(); if (applet != null) { applet.demoClosed(); } else System.exit(0); } //------------------------------------------------------------ // package private //------------------------------------------------------------ double doubleValue(String s) { try { return Double.valueOf(s).doubleValue(); } catch (Exception foo) { return Double.POSITIVE_INFINITY; } } void constrainedAdd(GridBagConstraints c, Container container, Component foo, Font font) { GridBagLayout gridbag = (GridBagLayout)container.getLayout(); if (font != null) foo.setFont(font); gridbag.setConstraints(foo, c); container.add(foo); } Choice cloneChoice (Choice source) { Choice result = new Choice(); for (int i = 0; i < source.getItemCount(); ++i) result.addItem(source.getItem(i)); result.setFont(source.getFont()); return result; } void addWithFont(Container container, Component foo, Font font) { if (font != null) foo.setFont(font); container.add(foo); } //{{DECLARE_CONTROLS Label label1; Label label2; TextField patternText; Label label3; Label label4; Label label5; Label labelArg; Label labelForm; TextField arg1Text; TextField arg2Text; TextField arg3Text; Choice arg1Type; Choice arg2Type; Choice arg3Type; Label label6; Choice localeMenu; Label localeLabel; Label label13; Label label14; TextField resultText; Label label7; Label label8; Label label9; TextField choice1Num; TextField choice2Num; TextField choice3Num; TextField choice4Num; TextField choice1Text; TextField choice2Text; TextField choice3Text; TextField choice4Text; Label label10; Label label11; Label label12; TextField arg4Text; Choice arg4Type; //}} //------------------------------------------------------------ // private //------------------------------------------------------------ private void buildGUI() { //{{INIT_CONTROLS setLayout(new FlowLayout(FlowLayout.CENTER,2,2)); // MD 8/7 setBackground(Color.white); // MD 8/7 // Main Title Panel titleCreditPanel = new Panel(); label6=new Label("Message Format Demo", Label.CENTER); label6.setFont(Utility.titleFont); titleCreditPanel.add(label6); Panel creditPanel = new Panel(); label13=new Label(creditString); label13.setFont(Utility.creditFont); creditPanel.add(label13); titleCreditPanel.add(creditPanel); Utility.fixGrid(titleCreditPanel,1); // result text Panel patternResultPanel = new Panel(); addWithFont(patternResultPanel,new Label("Result", Label.RIGHT),Utility.labelFont); addWithFont(patternResultPanel,resultText= new TextField(FIELD_COLUMNS),Utility.editFont); addWithFont(patternResultPanel,new Label("Pattern", Label.RIGHT),Utility.labelFont); addWithFont(patternResultPanel,patternText=new TextField(FIELD_COLUMNS),Utility.editFont); patternText.addKeyListener(this); Utility.fixGrid(patternResultPanel,2); // Locale and credits Panel localeCreditPanel = new Panel(); // localeCreditPanel.setLayout(new GridBagLayout()); localeLabel=new Label("Locale:",Label.LEFT); localeLabel.setFont(Utility.labelFont); //localeCreditPanel.add("loc",localeLabel); // LOCALE localeMenu= new Choice(); localeMenu.addItemListener(this); // Stick the names of the locales into the locale popup menu Locale displayLocale = Locale.getDefault(); for (int i = 0; i < locales.length; i++) { if (locales[i].getCountry().length() > 0) { localeMenu.addItem( locales[i].getDisplayName() ); if (locales[i].equals(Locale.getDefault())) { localeMenu.select(i); } } } localeMenu.setFont(Utility.choiceFont); Panel localePanel=new Panel(); localePanel.add(localeLabel); localePanel.add(localeMenu); localeCreditPanel.add(localePanel); arg1Type= new Choice(); arg1Type.setFont(Utility.choiceFont); arg1Type.addItem("Number"); arg1Type.addItem("Date"); arg1Type.addItem("Choice"); arg1Type.addItem("None"); arg1Type.addItemListener(this); // PUT THE ARGUMENTS/ FORMATS into GRID Panel allArgs = new Panel(); addWithFont(allArgs,new Label(" "),Utility.labelFont); addWithFont(allArgs,new Label("Arguments", Label.LEFT), Utility.labelFont); addWithFont(allArgs,new Label("0",Label.RIGHT),Utility.labelFont); addWithFont(allArgs,arg1Text=new TextField(12),Utility.editFont); addWithFont(allArgs,new Label("1",Label.RIGHT),Utility.labelFont); addWithFont(allArgs,arg2Text=new TextField(12),Utility.editFont); addWithFont(allArgs,new Label("2",Label.RIGHT),Utility.labelFont); addWithFont(allArgs,arg3Text=new TextField(12),Utility.editFont); addWithFont(allArgs,new Label("3",Label.RIGHT),Utility.labelFont); addWithFont(allArgs,arg4Text=new TextField(12),Utility.editFont); arg1Text.addKeyListener(this); arg2Text.addKeyListener(this); arg3Text.addKeyListener(this); arg4Text.addKeyListener(this); Utility.fixGrid(allArgs,2); Panel formatPanel = new Panel(); addWithFont(formatPanel,new Label(" "),Utility.labelFont); addWithFont(formatPanel,new Label("Formats", Label.LEFT), Utility.labelFont); addWithFont(formatPanel,new Label("0",Label.RIGHT),Utility.labelFont); addWithFont(formatPanel,arg1Type,Utility.choiceFont); addWithFont(formatPanel,new Label("1",Label.RIGHT),Utility.labelFont); addWithFont(formatPanel,arg2Type = cloneChoice(arg1Type), Utility.choiceFont); addWithFont(formatPanel,new Label("2",Label.RIGHT),Utility.labelFont); addWithFont(formatPanel,arg3Type = cloneChoice(arg1Type), Utility.choiceFont); addWithFont(formatPanel,new Label("3",Label.RIGHT),Utility.labelFont); addWithFont(formatPanel,arg4Type = cloneChoice(arg1Type), Utility.choiceFont); arg2Type.addItemListener(this); arg3Type.addItemListener(this); arg4Type.addItemListener(this); Utility.fixGrid(formatPanel,2); // Choices panel Panel choicesPanel = new Panel(); addWithFont(choicesPanel,new Label(">=", Label.LEFT), Utility.labelFont); addWithFont(choicesPanel,new Label("Choice Strings", Label.LEFT), Utility.labelFont); addWithFont(choicesPanel,choice1Num=new TextField(4), Utility.editFont); addWithFont(choicesPanel,choice1Text=new TextField(16), Utility.editFont); addWithFont(choicesPanel,choice2Num=new TextField(4), Utility.editFont); addWithFont(choicesPanel,choice2Text=new TextField(16), Utility.editFont); addWithFont(choicesPanel,choice3Num=new TextField(4), Utility.editFont); addWithFont(choicesPanel,choice3Text=new TextField(16), Utility.editFont); addWithFont(choicesPanel,choice4Num=new TextField(4), Utility.editFont); addWithFont(choicesPanel,choice4Text=new TextField(16), Utility.editFont); choice1Text.addKeyListener(this); choice2Text.addKeyListener(this); choice3Text.addKeyListener(this); choice4Text.addKeyListener(this); choice1Num.addKeyListener(this); choice2Num.addKeyListener(this); choice3Num.addKeyListener(this); choice4Num.addKeyListener(this); Utility.fixGrid(choicesPanel,2); add(titleCreditPanel); add(patternResultPanel); add(localeCreditPanel); Panel bottomPanel = new Panel(); bottomPanel.add(allArgs); XBorderPanel x = new XBorderPanel(); // MD 8/7 x.setBackground(Color.lightGray); x.setLayout(null); x.setSize(8,130); bottomPanel.add(x); bottomPanel.add(formatPanel); XBorderPanel x1 = new XBorderPanel(); // MD 8/7 x1.setBackground(Color.lightGray); x1.setLayout(null); x1.setSize(8,130); bottomPanel.add(x1); bottomPanel.add(choicesPanel); Utility.fixGrid(bottomPanel,5); // MD 8/7 only after fixGrid Utility.setInsets(bottomPanel,x,new Insets(20,20,2,2)); Utility.setInsets(bottomPanel,x1,new Insets(20,20,2,20)); add(bottomPanel); Panel copyrightPanel = new Panel(); addWithFont (copyrightPanel,new Label(copyrightString, Label.LEFT), Utility.creditFont); addWithFont (copyrightPanel,new Label(copyrightString2, Label.LEFT), Utility.creditFont); Utility.fixGrid(copyrightPanel,1); add(copyrightPanel); } private Object createObject(Choice typeMenu, TextField textField ) { int type = typeMenu.getSelectedIndex(); String text = textField.getText(); Object result = null; try { if (type == NUMBER || type == CHOICE) { result = new Double(text); } else if (type == DATE) { // Still use the deprecated new Date(text) until // the DateFormat.parse(text) is working properly. result = new Long( (new Date(text)).getTime() + 1); // 1 millisecond was added to display the date correctly. // This is done to fix the following scenario, eg, // "27 Sept 96" ==> "26 Sept 96 12:00 AM PDT" which is // equvalent to "27 Sept 96 00:00 AM PDT". -- CLH 9/27/96 } else if (type == NONE) { result = text; } } catch (RuntimeException e) { } return result; } private void errorText(String s) { if (DEBUG) { System.out.println(s); } } private static final String creditString = "v1.1a7, Demos"; private static final String copyrightString = ""; private static final String copyrightString2 = ""; private static final int FIELD_COLUMNS = 60; static private final int NUMBER = 0; static private final int DATE = 1; static private final int CHOICE = 2; static private final int NONE = 3; private static final boolean DEBUG = false; private Locale[] locales; private DemoApplet applet; private ChoiceFormat choiceFormat; // XXX } // MD 8/7 whole class, from Ralf. Use different name! class XBorderPanel extends Panel { /** * Panel shadow border width */ protected int shadow = 4; /** * Panel raised vs depressed look */ protected boolean raised = true; public XBorderPanel() { this.raised=true; } public XBorderPanel(boolean raised) { this.raised=raised; } /** * Re-layout parent. Called when a panel changes * size etc. */ protected void layoutParent() { Container parent = getParent(); if (parent != null) { parent.doLayout(); } } public void paint(Graphics g) { super.paint(g); Dimension size = getSize(); paintBorder(g, size); } protected void paintBorder(Graphics g, Dimension size) { Color c = getBackground(); g.setColor(c); g.fillRect(0, 0, size.width, size.height); draw3DRect(g, 0, 0, size.width, size.height, raised); } /** * Draw a 3D Rectangle. * @param g the specified Graphics window * @param x, y, width, height * @param raised - true if border should be painted as raised. * @see #paint */ public void draw3DRect(Graphics g, int x, int y, int width, int height, boolean raised) { Color c = g.getColor(); Color brighter = avgColor(c,Color.white); Color darker = avgColor(c,Color.black); // upper left corner g.setColor(raised ? brighter : darker); for (int i=0; i