/* * Copyright (c) 1995-1997 Sun Microsystems, Inc. All Rights Reserved. * * 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. */ /* * @(#)CollateDemo.java 1.1 97/5/23 * * (C) Copyright Taligent, Inc. 1996,1997 - 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.*; import java.util.Vector; import java.util.Locale; import java.text.NumberFormat; import java.text.Collator; import java.text.RuleBasedCollator; import java.text.ParseException; import java.text.CollationElementIterator; import java.text.BreakIterator; /** * Concrete class for demonstrating language sensitive collation. * The following is the instruction on how to run the collation demo. *

* =================== *

Customization

* You can produce a new collation by adding to or changing an existing * one. *

To show...

*
You can modify an existing collation to show how this works. * By adding items at the end of a collation you override earlier * information. * Watch how you can make the letter P sort at the end of the * alphabet.
*

Do...

*
1. Scroll to the end of the Sequence field. After the Z, * type * "< p , P". This will put the letter P (with both of its * case * variants) at the end of the alphabet. Hit the Set Rule button. This creates * a new collation with the name "Custom-1" (you could give it a * different name by typing in the Collator Name field). When you now look * at the Text field, you will see that you have changed the sequence to put * Pat * at the end. (If you did not have Sort Ascending on, click it * now.)
*
* Making P sort at the end may not seem terribly useful, but it is used to * make modifications in the sorting sequence for different languages. *

To show...

*
For example, you can add CH as a single letter after C, as * in * traditional Spanish sorting.
*

Do...

*
Enter in the following after Z; "& C < ch , cH, Ch, * CH". * Hit the Set Rule button, type in test words in the Text field (such as * "czar", * "churo" and "darn"), and select Sort Ascending to * see * the resulting sort order.
*

To show...

*
You can also add other sequences to the collation rules, * such as sorting symbols with their alphabetic equivalents.
*

Do...

*
1. Scroll to the end of the Sequence field. After the end, * type the following list (you can just select this text in your browser and * paste it in, to avoid typing). Now type lines in the Text field with these * symbols on them, and select Sort Ascending to see the resulting sort * order.
* *

Details

* If you are an advanced user and interested in trying out more rules, * here is a brief explanation of how they work. The sequence is a list of * rules. Each rule is of two forms: * *
Modifier
*
@ Indicates that accents are sorted backwards, as in * French
*
Text-argument
* The text can be any number of characters (if you want to include special * characters, such as space, use single-quotes around them). *
Relation
* The relations are the following: *
*
< Greater, as a letter difference (primary) *
; Greater, as an accent difference (secondary) *
, Greater, as a case difference (tertiary) *
= Equal *
& Reset previous comparison. *
*
Reset
* The "&" is special in that does not put the text-argument * into the sorting sequence; instead, it indicates that the next * rule is with respect to where the text-argument would be sorted. * This sounds more complicated than it is in practice. For example, the * following are equivalent ways of expressing the same thing: * * Notice that the order is important, since the subsequent item goes * immediately * after the text-argument. The following are not equivalent: * * The text-argument must already be present in the sequence, or some * initial substring of the text-argument must be present. (e.g. "a < * b& ae < e" is valid since "a" is present in the * sequencebefore "ae" is reset). In this latter case, * "ae" * is not entered and treated as a single character; instead, * "e" is sorted as if it were expanded to two characters: * "a" * followed by an "e".
* This difference appears in natural languages: in traditional Spanish * "ch" * is treated as though it contracts to a single character * (expressed * as "c < ch < d"), while in traditional German * "ä" * (a-umlaut) is treated as though it expands to two characters * (expressed * as "a & ae ; ä < b").
*
Ignorable Characters
* The first rule must start with a relation (the examples we have used are * fragments; "a < b" really should be "< a < * b"). * If, however, the first relation is not "<", then all the * all * text-arguments up to the first "<" are ignorable. For * example, * ", - < a < b" makes "-" an ignorable * character, * as we saw earlier in the word "black-birds".
*
Accents
* The Collator automatically normalizes text internally to separate * accents * from base characters where possible. So, if you type in an * "ä" * (a-umlaut), after you reset the collation you will see * "a\u0308" * in the sequence, where \u0308 is the Java syntax for umlaut. The * demonstration * program uses this syntax instead of just showing the umlaut since many * browsers are unable to display the umlaut yet.
*

Errors

* The following are errors: * * If you produce one of these errors, then the demonstration will beep at * you, and select the offending text (note: on some browsers, the * selection will not appear correctly). * @version 1.1 11/23/96 * @author Kathleen Wilson, Helena Shih * @see java.util.Collator * @see java.util.RuleBasedCollator * @see java.demos.utilities.DemoApplet */ public class CollateDemo extends DemoApplet { /** * The main function which defines the behavior of the CollateDemo applet * when an applet is started. */ public static void main(String argv[]) { DemoApplet.showDemo(new CollateFrame(null)); } /** * This creates a CollateFrame for the demo applet. */ public Frame createDemoFrame(DemoApplet applet) { return new CollateFrame(applet); } } /** * A Frame is a top-level window with a title. The default layout for a frame * is BorderLayout. The CollateFrame class defines the window layout of * CollateDemo. */ class CollateFrame extends Frame { /** * Constructs a new CollateFrame that is initially invisible. */ public CollateFrame(DemoApplet applet) { super("Collator Demo"); this.applet = applet; init(); } /** * 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() { theLocale = Locale.US; theCollation = Collator.getInstance(theLocale); buildGUI(); } /** * Handles the event. Returns true if the event is handled and should not * be passed to the parent of this component. The default event handler * calls some helper methods to make life easier on the programmer. */ public boolean handleEvent(Event evt) { if (evt.target == searchTextEntry && (evt.id == Event.KEY_PRESS || evt.id == Event.KEY_ACTION)) { switch (evt.key) { case 0x3ef: return handleFindNext(); case 0x3ee: return handleFindPrevious(); } } if (evt.id == Event.MOUSE_UP && tabsAndCards.didCardChange() == true) { if (searchPanel.isShowing() == true) { isCustomizing = false; setSearchMsg("Use arrow keys to find next and previous."); } else if (sortPanel.isShowing() == true) { isCustomizing = false; } else if (customPanel.isShowing() == true) { isCustomizing = true; errorText(""); return handleLocale(); //to display current locale rules } } if (evt.id == Event.ACTION_EVENT) { errorMsg.setVisible(false); if (evt.target == sortAscending) return handleSort(true, false); else if (evt.target == sortDescending) return handleSort(false, true); else if (evt.target == localeChoice) { handleLocale(); return handleSort(sortAscending.getState(), sortDescending.getState()); } else if (evt.target == decompChoice) { return handleSort(sortAscending.getState(), sortDescending.getState()); } else if (evt.target == strengthChoice) { return handleSort(sortAscending.getState(), sortDescending.getState()); } else if (evt.target == collateRulesButton) { collateRulesButton.setLabel("setting"); handleSetRules(); collateRulesButton.setLabel("Set Rules"); return handleSort(sortAscending.getState(), sortDescending.getState()); } } else if (evt.id == Event.KEY_PRESS) { if (evt.target == textentry) { checkboxes.setSelectedCheckbox(noSort); } } else if (evt.id == Event.WINDOW_DESTROY && evt.target == this) { this.setVisible(false); this.dispose(); if (applet != null) { applet.demoClosed(); } else System.exit(0); return true; } return super.handleEvent(evt); } /** * This function is called when you press the "SortDescend" button. * It does an extremely simple sort, using the Collator.compare function. * To improve performance, you could transform the strings into * a sort key which is a series of characters that can be * compared using bit-wise comparison. In addition, you can also use * collation keys to compare two strings based on the collation rules. * @see java.util.Collator#compare * @see java.util.Collator#getSortKey * @see java.util.Collator#getCollationKey */ public boolean handleSort(boolean ascending, boolean descending) { if (ascending == descending) return true; int exchangeResult = ascending ? -1 : 1; InitializeListVector(); String targetItem; String sourceItem; byte compareResult; String strengthName = strengthChoice.getSelectedItem(); if (strengthName.equals(tertiary)) { theCollation.setStrength(Collator.TERTIARY); }else if (strengthName.equals(secondary)) { theCollation.setStrength(Collator.SECONDARY); } else theCollation.setStrength(Collator.PRIMARY); int decompItem = decompChoice.getSelectedIndex(); if (decompItem == 0) { theCollation.setDecomposition(Collator.NO_DECOMPOSITION); }else if (decompItem == 1) { theCollation.setDecomposition(Collator.CANONICAL_DECOMPOSITION); } else theCollation.setDecomposition(Collator.FULL_DECOMPOSITION); int numItems = textList.size(); for (int sourceIndex = 1; sourceIndex < numItems; sourceIndex++) { sourceItem = (String) textList.elementAt(sourceIndex); for (int targetIndex = 0; targetIndex < sourceIndex; targetIndex++) { targetItem = (String) textList.elementAt(targetIndex); compareResult = (byte) theCollation.compare(sourceItem, targetItem); if (compareResult == exchangeResult) { textList.removeElementAt(sourceIndex); textList.insertElementAt(sourceItem, targetIndex); break; } } } resetTextArea(); return true; } /** * This function is called when you press the Locale button. * It sets the locale that is to be used to create the collation object. * @see java.util.Collator#getInstance */ public boolean handleLocale() { int index = localeChoice.getSelectedIndex(); theCollation = collations[index]; if (isCustomizing == true && theCollation != null) { String theRules = ruleStrings[index]; if (theRules == null) { theRules = createUnicodeString( ((RuleBasedCollator)theCollation).getRules()); ruleStrings[index] = theRules; } ruleEntry.setText(theRules); } return true; } /** * This function is called when you press the "Set Rules" button. * It sets the rules that are to be used by the current collation object. * @see java.util.RuleBasedCollator#getRules */ public void handleSetRules() { int index = localeChoice.getSelectedIndex(); String rules = ruleEntry.getText(); if ((rules.equals(ruleStrings[index]) == false) && (theCollation instanceof RuleBasedCollator) ) { int idx = 0; try { String collName = collationName.getText(); for (int n = 0; n < localeChoice.getItemCount(); n++) { if (collName.equals(localeChoice.getItem(n))) { theCollation = new RuleBasedCollator(convertStringToRules(rules)); collations[n] = theCollation; idx = n; break; } } if (idx == 0) { if (localeChoice.getItemCount() < MAX_COLLATIONS) { idx = localeChoice.getItemCount(); theCollation = new RuleBasedCollator(convertStringToRules(rules)); collations[idx] = theCollation; localeChoice.addItem( collName ); } else { throw new ParseException("Max # exceeded!" + "Please replace an existing one.", 0); } if (collName.startsWith("custom-")) { untitledIndex++; } } localeChoice.select(idx); ruleStrings[idx] = null; //force recalculation of locale rule string handleLocale(); if (localeChoice.getItemCount() < MAX_COLLATIONS) collationName.setText("custom-" + untitledIndex); } catch (ParseException foo) { collateRulesButton.setLabel("Set Rules"); errorText("Error: " + foo.getMessage()); } } } /** * Must be called after the array of locales has been initialized. */ public void loadCollationTables() { MAX_COLLATIONS = locales.length + 10; collations = new Collator[MAX_COLLATIONS]; for (int i = 0; i < locales.length; i++) { collations[i] = Collator.getInstance(locales[i]); } } //------------------------------------------------------------ // package private //------------------------------------------------------------ Panel makePanel(Component demo, Component code) { Panel temp = new Panel(); GridBagLayout gridbag = new GridBagLayout(); GridBagConstraints constraints = new GridBagConstraints(); temp.setLayout(gridbag); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.gridwidth = GridBagConstraints.REMAINDER; //end row gridbag.setConstraints(demo, constraints); constraints.gridheight = GridBagConstraints.REMAINDER; //end column gridbag.setConstraints(code, constraints); temp.add(demo); temp.add(code); return temp; } void addWithFont(Container container, Component foo, Font font) { if (font != null) foo.setFont(font); container.add(foo); } void buildGUI() { setBackground(Utility.bgColor); setLayout(new BorderLayout()); //SORT, SEARCH, CUSTOMIZE tabsAndCards = new TabPanel(); tabsAndCards.add("Sort",createSortPanel()); tabsAndCards.add("Search",createSearchPanel()); tabsAndCards.add("Customize",createCustomPanel()); // TITLE Label title=new Label("Collator Demo", Label.CENTER); title.setFont(Utility.titleFont); Panel titlePanel = new Panel(); titlePanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0)); titlePanel.add(title); add("North", titlePanel); // LOCALE Locale myLocale = new Locale("","",""); Label localeLabel = new Label("Locale: ", Label.LEFT); localeLabel.setFont(Utility.labelFont); localeChoice = new Choice(); localeChoice.setBackground(Utility.choiceColor); //Get all locales for debugging, but only get G7 locales for demos. if (DEBUG == true) locales = Collator.getAvailableLocales(); else locales = Utility.getG7Locales(); int defaultLoc = 0; for (int i = 0; i < locales.length; i++) { if (locales[i].getCountry().length() > 0) { localeChoice.addItem (locales[i].getDisplayName()); if (locales[i].equals(theLocale)) { defaultLoc = i; } } } localeChoice.setFont(Utility.choiceFont); //must be called after the array of locales has been initialized loadCollationTables(); localeChoice.select(defaultLoc); Label decompLabel = new Label("Decomposition Mode:", Label.LEFT); decompLabel.setFont(Utility.labelFont); decompChoice = new Choice(); decompChoice.setBackground(Utility.choiceColor); decompChoice.addItem(no_decomposition); decompChoice.addItem(canonical_decomposition); decompChoice.addItem(full_decomposition); decompChoice.select(canonical_decomposition); decompChoice.setFont(Utility.choiceFont); Label strengthLabel = new Label("Strength:", Label.LEFT); strengthLabel.setFont(Utility.labelFont); strengthChoice = new Choice(); strengthChoice.setBackground(Utility.choiceColor); strengthChoice.addItem(tertiary); strengthChoice.addItem(secondary); strengthChoice.addItem(primary); strengthChoice.setFont(Utility.choiceFont); Panel infoPanel = new Panel(); infoPanel.add(localeLabel); infoPanel.add(localeChoice); infoPanel.add(decompLabel); infoPanel.add(decompChoice); infoPanel.add(strengthLabel); infoPanel.add(strengthChoice); Utility.fixGrid(infoPanel,1); Panel centerPanel = new Panel(); centerPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 15, 0)); centerPanel.add(tabsAndCards); centerPanel.add(infoPanel); add("Center",centerPanel); //COPYRIGHTS Panel bottomPanel = new Panel(); bottomPanel.setLayout(new GridLayout(2,1,0,0)); addWithFont (bottomPanel, new Label(Utility.copyright1, Label.LEFT), Utility.creditFont); addWithFont (bottomPanel, new Label(Utility.copyright2, Label.LEFT), Utility.creditFont); Utility.fixGrid(bottomPanel,1); add("South", bottomPanel); } Panel createSortPanel() { sortPanel = new Panel(); sortPanel.setLayout(new BorderLayout()); // CHECKBOXES checkboxes= new CheckboxGroup(); sortAscending = new Checkbox("Sort Ascending",checkboxes, false); sortAscending.setFont(Utility.labelFont); sortAscending.setSize(100, 20); sortDescending = new Checkbox("Sort Descending",checkboxes, false); sortDescending.setSize(100, 20); sortDescending.setFont(Utility.labelFont); noSort = new Checkbox("Not Sorted",checkboxes, true); noSort.setFont(Utility.labelFont); noSort.setSize(100, 20); Panel bPanel = new Panel(); bPanel.setLayout(new GridLayout(3,1,0,0)); bPanel.add(sortAscending); bPanel.add(sortDescending); bPanel.add(noSort); Panel buttonPanel = new Panel(); buttonPanel.setLayout(new BorderLayout()); buttonPanel.add("North",bPanel); // TEXT Panel tePanel = new Panel(); tePanel.setLayout(new BorderLayout()); textentry.setFont(editFont); textentry.setText("black-birds\n" + "Pat\n" + "\u00c4hre\n" + "p\u00E9ch\u00E9\n" + "p\u00EAche\n" + "\u00c5rhus\n" + "p\u00E9cher\n" + "p\u00EAcher\n" + "Tod\n" + "T\u00F6ne\n" + "Tofu\n" + "blackbirds\n" + "Ton\n" + "PAT\n" + "blackbird\n" + "\u00d8re\n" + "black-bird\n" + "pat\n"); Label sortLabel = new Label("Text to Sort:", Label.LEFT); sortLabel.setFont(Utility.labelFont); tePanel.add("North", sortLabel); tePanel.add("Center", textentry); // PUT ALL TOGETHER Panel middlePanel = new Panel(); middlePanel.setLayout(new FlowLayout(FlowLayout.LEFT,0,0)); middlePanel.add(buttonPanel); middlePanel.add(tePanel); sortPanel.add("Center", middlePanel); return sortPanel; } Panel createCustomPanel() { customPanel = new Panel(); customPanel.setLayout(new BorderLayout()); //RULE ENTRY AREA Panel ruleEntryPanel = new Panel(); Panel rulePanel = new Panel(); ruleEntry.setFont(ruleFont); Panel namePanel = new Panel(); namePanel.setLayout(new FlowLayout(FlowLayout.LEFT,0,2)); collateRulesButton = new Button("Set Rules"); collationName = new TextField(10); collationName.setText("custom-" + untitledIndex); collationName.setFont(Utility.choiceFont); Label colLabel = new Label("Collator Name: ", Label.LEFT); colLabel.setFont(Utility.labelFont); namePanel.add(colLabel); namePanel.add(collationName); Label rulesLabel = new Label("Collator Rules:", Label.LEFT); rulesLabel.setFont(Utility.labelFont); ruleEntryPanel.add(rulesLabel); ruleEntryPanel.add(ruleEntry); ruleEntryPanel.add(namePanel); ruleEntryPanel.add(collateRulesButton); errorMsg.setFont(Utility.labelFont); errorMsg.setText("This is where error messages go............."); ruleEntryPanel.add(errorMsg); Utility.fixGrid(ruleEntryPanel,1); customPanel.add("Center", ruleEntryPanel); return customPanel; } Panel createSearchPanel() { searchPanel = new Panel(); searchPanel.setLayout(new BorderLayout()); //SEARCH DEMO Panel searchStringPanel = new Panel(); searchStringPanel.setLayout(new FlowLayout(FlowLayout.LEFT,20,0)); Label searchLabel = new Label("Search String:", Label.RIGHT); searchLabel.setFont(Utility.labelFont); searchStringPanel.add(searchLabel); searchString.setText("T\u00F6ne"); searchString.setFont(Utility.choiceFont); searchStringPanel.add(searchString); searchTextEntry.setFont(editFont); searchTextEntry.setText("black-birds Pat p\u00E9ch\u00E9 \n" + "p\u00EAche p\u00E9cher p\u00EAcher \n" + "Tod T\u00F6ne Tofu blackbirds \n" + "Ton PAT blackbird \n" + "black-bird pat "); Panel findTextPanel = new Panel(); findTextPanel.add(searchStringPanel); findTextPanel.add(searchTextEntry); searchByWord.setState(false); searchByWord.setFont(Utility.labelFont); findTextPanel.add(searchByWord); searchMsg.setFont(Utility.labelFont); setSearchMsg("Use arrow keys to find next and previous."); findTextPanel.add(searchMsg); Utility.fixGrid(findTextPanel,1); searchPanel.add("Center", findTextPanel); return searchPanel; } //------------------------------------------------------------ // TextArea and TextField bug work-around //------------------------------------------------------------ /** * This function fixes a bug in the TextArea and TextField which * messes up the high byte of Unicode characters when you get the * text out. */ private String getFixedString(TextComponent theArea) { String composite = theArea.getText(); char compositeArray[] = composite.toCharArray(); for (int i = 0; i < compositeArray.length; i++) { compositeArray[i] = (char) (compositeArray[i] & 0xFF); } return new String(compositeArray); } //------------------------------------------------------------ // SEARCH DEMO //------------------------------------------------------------ private void setSearchMsg(String s) { if (!s.equals(searchMsg.getText())) { searchMsg.setText(s); } } /** * This function is called when you press the "->" key. * It searches for the next string meeting the criteria of matching * to the search string according the the current locale and strength. * @see java.text.CollationElementIterator */ public boolean handleFindNext() { if (searchByWord.getState() == true) { return handleFindNextWord(); } String searchStr = getFixedString(searchString); String textStr = getFixedString(searchTextEntry); int searchOffset = searchTextEntry.getSelectionEnd(); boolean matchFound = false; int strength = Collator.PRIMARY; String strengthName = strengthChoice.getSelectedItem(); if (strengthName.equals(tertiary)) { strength = Collator.TERTIARY; theCollation.setStrength(Collator.TERTIARY); }else if (strengthName.equals(secondary)) { strength = Collator.SECONDARY; theCollation.setStrength(Collator.SECONDARY); } else theCollation.setStrength(Collator.PRIMARY); //make sure you only go through the text once for (int i = 0; i < textStr.length() + 1; i++) { if (searchOffset >= textStr.length()) searchOffset = 0; String sub = textStr.substring(searchOffset, textStr.length()); if (stringsMatch(searchStr, sub, strength) == true) { matchFound = true; break; } searchOffset++; } if (matchFound == true) { selectMatchingText(searchStr, textStr, searchOffset); }else { setSearchMsg("No Match Found"); searchTextEntry.select(0, 0); } return matchFound; } /** * This function is called when you press the "->" key and * you have selected to find whole words only. * It searches for the next whole word meeting the criteria of matching * to the search string according the the current locale and strength. * @see java.text.CollationElementIterator */ public boolean handleFindNextWord() { int strength = Collator.PRIMARY; String strengthName = strengthChoice.getSelectedItem(); if (strengthName.equals(tertiary)) { strength = Collator.TERTIARY; theCollation.setStrength(Collator.TERTIARY); }else if (strengthName.equals(secondary)) { strength = Collator.SECONDARY; theCollation.setStrength(Collator.SECONDARY); } else theCollation.setStrength(Collator.PRIMARY); //Strip the leading and trailing spaces off of //the search string. String searchStr = getFixedString(searchString); int ssStart = 0; while (Character.isWhitespace(searchStr.charAt(ssStart))) { ssStart++; } int ssEnd = searchStr.length(); searchStr = searchStr.substring(ssStart,ssEnd); int searchOffset = searchTextEntry.getSelectionEnd(); boolean matchFound = false; String textStr = getFixedString(searchTextEntry); wordIterator.setText(textStr); int wordEnd = 0; //make sure you only go through the text once for (int i = 0; i < textStr.length() + 1;) { if (searchOffset >= textStr.length()) searchOffset = 0; wordEnd = wordIterator.following(searchOffset); if (theCollation.equals(searchStr, textStr.substring(searchOffset,wordEnd))) { matchFound = true; break; } i += wordEnd - searchOffset; searchOffset = wordEnd; } if (matchFound == true) { searchTextEntry.select(searchOffset, wordEnd); setSearchMsg("Found Match: \"" + textStr.substring(searchOffset,wordEnd) + "\" at index " + String.valueOf(searchOffset)); }else { setSearchMsg("No Match Found"); searchTextEntry.select(0, 0); } return matchFound; } /** * This function is called when you press the "<-" key. * It searches for the previous string meeting the criteria of matching * to the search string according the the current locale and strength. * @see java.text.CollationElementIterator */ public boolean handleFindPrevious() { if (searchByWord.getState() == true) { return handleFindPreviousWord(); } String searchStr = getFixedString(searchString); String textStr = getFixedString(searchTextEntry); int searchOffset = searchTextEntry.getSelectionStart() - searchStr.length(); boolean matchFound = false; int strength = Collator.PRIMARY; String strengthName = strengthChoice.getSelectedItem(); if (strengthName.equals(tertiary)) { strength = Collator.TERTIARY; theCollation.setStrength(Collator.TERTIARY); }else if (strengthName.equals(secondary)) { strength = Collator.SECONDARY; theCollation.setStrength(Collator.SECONDARY); } else theCollation.setStrength(Collator.PRIMARY); //make sure you only go through the text once for (int i = 0; i < textStr.length() + 1; i++) { if (searchOffset < 0) searchOffset = textStr.length() - 1; String sub = textStr.substring(searchOffset, textStr.length()); if (stringsMatch(searchStr, sub, strength) == true) { matchFound = true; break; } searchOffset--; } if (matchFound == true) { selectMatchingText(searchStr, textStr, searchOffset); }else { setSearchMsg("No Match Found"); searchTextEntry.select(0, 0); } return matchFound; } /** * This function is called when you press the "<-" key and * you have selected to find whole words only. * It searches for the previous whole word meeting the criteria of matching * to the search string according the the current locale and strength. * @see java.text.CollationElementIterator */ public boolean handleFindPreviousWord() { int strength = Collator.PRIMARY; String strengthName = strengthChoice.getSelectedItem(); if (strengthName.equals(tertiary)) { strength = Collator.TERTIARY; theCollation.setStrength(Collator.TERTIARY); }else if (strengthName.equals(secondary)) { strength = Collator.SECONDARY; theCollation.setStrength(Collator.SECONDARY); } else theCollation.setStrength(Collator.PRIMARY); //Strip the leading and trailing spaces off of //the search string. String searchStr = getFixedString(searchString); int ssStart = 0; while (Character.isWhitespace(searchStr.charAt(ssStart))) { ssStart++; } int ssEnd = searchStr.length(); searchStr = searchStr.substring(ssStart,ssEnd); String textStr = getFixedString(searchTextEntry); wordIterator.setText(textStr); int searchOffset = searchTextEntry.getSelectionEnd(); int wordEnd; if (searchOffset == textStr.length()) { wordIterator.last(); } else wordIterator.following(searchOffset); if (searchTextEntry.getSelectionStart() == searchTextEntry.getSelectionEnd()) { wordEnd = wordIterator.current(); } else wordEnd = wordIterator.previous(); searchOffset = wordIterator.previous(); boolean matchFound = false; //make sure you only go through the text once for (int i = 0; i < textStr.length() + 1;) { if (searchOffset <= 0) { wordEnd = wordIterator.last(); } else { wordEnd = searchOffset; } searchOffset = wordIterator.previous(); if (theCollation.equals(searchStr, textStr.substring(searchOffset,wordEnd))) { matchFound = true; break; } i += wordEnd - searchOffset; } if (matchFound == true) { searchTextEntry.select(searchOffset, wordEnd); setSearchMsg("Found Match: \"" + textStr.substring(searchOffset,wordEnd) + "\" at index " + String.valueOf(searchOffset)); }else { setSearchMsg("No Match Found"); searchTextEntry.select(0, 0); } return matchFound; } /** * This function is used by handleFindNext(); * Determine if the textToSearch begins with the text in searchStrIter. */ private boolean stringsMatch(String searchStr, String textToSearch, int strength) { CollationElementIterator searchStringIter = ((RuleBasedCollator)theCollation).getCollationElementIterator(searchStr); CollationElementIterator textToSearchIter = ((RuleBasedCollator)theCollation).getCollationElementIterator(textToSearch); boolean result = true; int ssOrder = searchStringIter.next(); int ttsOrder = textToSearchIter.next(); while (ssOrder != CollationElementIterator.NULLORDER && ttsOrder != CollationElementIterator.NULLORDER) { //Ignore the ignorable characters while (mapOrder(ssOrder, strength) == 0) { ssOrder = searchStringIter.next(); } if (ssOrder == CollationElementIterator.NULLORDER) { return result; } while (mapOrder(ttsOrder, strength) == 0) { ttsOrder = textToSearchIter.next(); } if (collationOrdersEqual(ssOrder, ttsOrder, strength) == false) { result = false; break; } ssOrder = searchStringIter.next(); ttsOrder = textToSearchIter.next(); } return result; } /** * Given a complete order, return the order for the given strength. */ private int mapOrder(int order, int strength) { if (strength == Collator.TERTIARY) return order & 0xFFFFFFFF; if (strength == Collator.PRIMARY) return order & 0xFFFF0000; return order & 0xFFFFFF00; } /** * This function is used by handleFindNext(); * Select the text in the target string that matches the search string. * Ignore leading and trailing white space. */ private void selectMatchingText(String searchStr, String targetStr, int positionInTarget) { //skip white space at beginning and end of search string. int ssStart = 0; while (Character.isWhitespace(searchStr.charAt(ssStart))) { ssStart++; } int ssEnd = searchStr.length(); String searchSubStr = searchStr.substring(ssStart, ssEnd); //skip white space at beginning of target string while (Character.isWhitespace(targetStr.charAt(positionInTarget))) { positionInTarget++; } int endIndex = positionInTarget; for (; endIndex < targetStr.length(); endIndex++) { if (theCollation.equals(searchSubStr, targetStr.substring(positionInTarget,endIndex))) { break; } } searchTextEntry.select(positionInTarget, endIndex); setSearchMsg("Found Match: \"" + targetStr.substring(positionInTarget,endIndex) + "\" at index " + String.valueOf(positionInTarget)); } /** * This function is used by handleFindNext(); * Given a collation strength and two collation orders, resulting from * calling CollationElementIterator.next(), determine if the two * collation orders are equivalent for the given strength. */ private boolean collationOrdersEqual(int order1, int order2, int strength) { if (order1 == order2) return true; if (CollationElementIterator.primaryOrder(order1) != CollationElementIterator.primaryOrder(order2)) return false; if (strength == Collator.PRIMARY) return true; if (CollationElementIterator.secondaryOrder(order1) != CollationElementIterator.secondaryOrder(order2)) return false; if (strength == Collator.SECONDARY) return true; return false; } //------------------------------------------------------------ // private //------------------------------------------------------------ /** * This function is called by handleSortAscend and handleSortDescend * to seperate each line of the textArea into individual strings, * and add them to the vector to be sorted. */ private void InitializeListVector() { textList.removeAllElements(); String composite = getFixedString(textentry); String substr; int startOffset = 0; int endOffset = 0; for (; endOffset < composite.length(); endOffset++) { char ch = composite.charAt(endOffset); if (ch == '\n' || ch == '\r') { if (endOffset > startOffset) { substr = composite.substring(startOffset, endOffset); textList.addElement(substr); } startOffset = endOffset + 1; } } if (startOffset < composite.length()) { substr = composite.substring(startOffset, composite.length()); textList.addElement(substr); } } /** * This function is called by handleSortAscend and handleSortDescend * to reset the text area based on the sort results stored as a vector * of substrings. */ private void resetTextArea() { String composite = new String(); int i = 0; for (; i < textList.size() - 1; i++) { composite = composite.concat((String) textList.elementAt(i)); composite = composite.concat("\n"); } //don't add the \n to the end composite = composite.concat((String) textList.elementAt(i)); textentry.setText(composite); textList.removeAllElements(); } private void errorText(String s) { errorMsg.setText(s); errorMsg.setVisible(true); } private static StringBuffer oneChar = new StringBuffer(7); private String makeDisplayString(char ch) { oneChar.setLength(0); if (ch < 0x0020 || (ch > 0x007D && ch < 0x00A0) || ch > 0x00FF) { String temp = Integer.toString((int)ch,16).toUpperCase(); oneChar.append(temp); if (temp.length() < 4) oneChar.append(zeros.substring(0,4-temp.length())); } else { oneChar.append(ch); } return oneChar.toString(); } /** Changes a string into a representation that can be pasted into a program. Uses \ t, \ uxxxx, etc. to display characters outside of Latin1. Others are themselves. */ private String createUnicodeString(String source) { StringBuffer result = new StringBuffer(source.length()); int len = 0; int i = 0; while (i < source.length()) { char ch = source.charAt(i); switch(ch) { case '\'': int mytemp = i; int n = 0; result.append(ch); ch = source.charAt(++i); while((ch != '\'') && (i < source.length() -1)) { String inquote = makeDisplayString(ch); n++; i++; if (inquote.length() > 1) { result.append("\\u"); result.append(inquote); len += 6; } else { result.append(inquote); len++; } ch = source.charAt(i); } if (n == 0) { result.append('\''); n++; i++; ch = source.charAt(i); } result.append(ch); len += n; break; case ';': case '=': if (len > 15) { result.append('\n'); len = 0; } result.append(' '); result.append(ch); len += 2; break; case ',': result.append(' '); result.append(ch); result.append(' '); len += 3; break; case '<': case '&': result.append('\n'); result.append(ch); result.append(' '); len = 0; len += 2; break; case '\n': break; default: if( isSpecialChar(ch) ) { String dspString = makeDisplayString(ch); if (dspString.length() > 1) { result.append("'\\u"); result.append(dspString); result.append("'"); len += 8; } else if( ch != '\u0020' ){ result.append("'"); result.append(dspString); result.append("'"); len += 3; } } else { String dspString = makeDisplayString(ch); if (dspString.length() > 1) { result.append("\\u"); result.append(dspString); len += 6; } else { result.append(dspString); len++; } } break; } i++; } return result.toString(); } private boolean isSpecialChar(char ch) { return (((ch <= '\u002F') && (ch >= '\u0020')) || (ch == '\u003A') || ((ch <= '\u0060') && (ch >= '\u005B')) || ((ch <= '\u007E') && (ch >= '\u007B'))); } private static final String zeros = "0000"; private String convertStringToRules(String source) { StringBuffer result = new StringBuffer(source.length()); for (int i = 0; i < source.length(); ++i) { //hack around TextArea bug char ch = (char) (source.charAt(i) & 0xFF); switch(ch) { case '\n': break; case '\'': if ( (i+6 < source.length()) && (source.charAt(i+2) == 'u') ) { String temp = source.substring(i+3,i+7); if (temp.equals("005c")) { //have to handle backslash differently since //it is a special character in Java result.append("'\\u005c'"); } else result.append((char)(Integer.parseInt(temp,16))); i = i + 7; } else result.append(ch); break; case '\\': if ( (i+5 < source.length()) && (source.charAt(i+1) == 'u') ) { String temp = source.substring(i+2,i+6); result.append((char)(Integer.parseInt(temp,16))); i = i + 6; } else result.append(ch); break; default: result.append(ch); break; } } return result.toString(); } // HSYS : fix private static int MAX_COLLATIONS = 12; private int untitledIndex = 1; private static final int FIELD_COLUMNS = 45; private static final Font editFont = new Font("TimesRoman",Font.PLAIN,18); private static final Font ruleFont = new Font("TimesRoman",Font.BOLD,14); private static final boolean DEBUG = false; private TextArea textentry = new TextArea(13, 15); private TextArea ruleEntry = new TextArea(10, 30); private Vector textList = new Vector(15, 10); private Choice localeChoice; private static final String no_decomposition = "None"; private static final String canonical_decomposition = "Canonical"; private static final String full_decomposition = "Full"; private Choice decompChoice; private static final String tertiary = "Tertiary - a vs. A"; private static final String secondary = "Secondary - a vs. \u00E0"; private static final String primary = "Primary - a vs. b"; private Choice strengthChoice; private Locale theLocale = null; private Collator theCollation = null; private Locale[] locales; private Collator[] collations; private String[] ruleStrings = new String[MAX_COLLATIONS]; private Label errorMsg = new Label("", Label.LEFT); CheckboxGroup checkboxes; Checkbox sortAscending; Checkbox sortDescending; Checkbox noSort; Button collateRulesButton; TextField collationName; private DemoApplet applet; private TabPanel tabsAndCards; private Panel sortPanel; private Panel searchPanel; private Panel customPanel; private boolean isCustomizing = false; //SEARCH DEMO private TextField searchString = new TextField(12); private TextArea searchTextEntry = new TextArea(10,30); private Label searchMsg = new Label("", Label.LEFT); private Checkbox searchByWord = new Checkbox("Match Whole Word Only"); private BreakIterator wordIterator = BreakIterator.getWordInstance(); }