Source Code Cross Referenced for NFRuleSet.java in  » Internationalization-Localization » icu4j » com » ibm » icu » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Internationalization Localization » icu4j » com.ibm.icu.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         *******************************************************************************
003:         * Copyright (C) 1996-2004, International Business Machines Corporation and    *
004:         * others. All Rights Reserved.                                                *
005:         *******************************************************************************
006:         */
007:        package com.ibm.icu.text;
008:
009:        import com.ibm.icu.impl.UCharacterProperty;
010:        import com.ibm.icu.impl.Utility;
011:
012:        import java.text.*;
013:        import java.util.Vector;
014:
015:        /**
016:         * A collection of rules used by a RuleBasedNumberFormat to format and
017:         * parse numbers.  It is the responsibility of a RuleSet to select an
018:         * appropriate rule for formatting a particular number and dispatch
019:         * control to it, and to arbitrate between different rules when parsing
020:         * a number.
021:         */
022:
023:        final class NFRuleSet {
024:            //-----------------------------------------------------------------------
025:            // constants
026:            //-----------------------------------------------------------------------
027:
028:            /**
029:             * Puts a copyright in the .class file
030:             */
031:            private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp.  All rights reserved.";
032:
033:            //-----------------------------------------------------------------------
034:            // data members
035:            //-----------------------------------------------------------------------
036:
037:            /**
038:             * The rule set's name
039:             */
040:            private String name;
041:
042:            /**
043:             * The rule set's regular rules
044:             */
045:            private NFRule[] rules;
046:
047:            /**
048:             * The rule set's negative-number rule
049:             */
050:            private NFRule negativeNumberRule = null;
051:
052:            /**
053:             * The rule set's fraction rules: element 0 is the proper fraction
054:             * (0.x) rule, element 1 is the improper fraction (x.x) rule, and
055:             * element 2 is the master (x.0) rule.
056:             */
057:            private NFRule[] fractionRules = new NFRule[3];
058:
059:            /**
060:             * True if the rule set is a fraction rule set.  A fraction rule set
061:             * is a rule set that is used to format the fractional part of a
062:             * number.  It is called from a >> substitution in another rule set's
063:             * fraction rule, and is only called upon to format values between
064:             * 0 and 1.  A fraction rule set has different rule-selection
065:             * behavior than a regular rule set.
066:             */
067:            private boolean isFractionRuleSet = false;
068:
069:            /**
070:             * Used to limit recursion for bad rule sets.
071:             */
072:            private int recursionCount = 0;
073:
074:            /**
075:             * Limit of recursion.
076:             */
077:            private static final int RECURSION_LIMIT = 50;
078:
079:            //-----------------------------------------------------------------------
080:            // construction
081:            //-----------------------------------------------------------------------
082:
083:            /*
084:             * Constructs a rule set.
085:             * @param descriptions An array of Strings representing rule set
086:             * descriptions.  On exit, this rule set's entry in the array will
087:             * have been stripped of its rule set name and any trailing whitespace.
088:             * @param index The index into "descriptions" of the description
089:             * for the rule to be constructed
090:             */
091:            public NFRuleSet(String[] descriptions, int index)
092:                    throws IllegalArgumentException {
093:                String description = descriptions[index];
094:
095:                if (description.length() == 0) {
096:                    throw new IllegalArgumentException(
097:                            "Empty rule set description");
098:                }
099:
100:                // if the description begins with a rule set name (the rule set
101:                // name can be omitted in formatter descriptions that consist
102:                // of only one rule set), copy it out into our "name" member
103:                // and delete it from the description
104:                if (description.charAt(0) == '%') {
105:                    int pos = description.indexOf(':');
106:                    if (pos == -1) {
107:                        throw new IllegalArgumentException(
108:                                "Rule set name doesn't end in colon");
109:                    } else {
110:                        name = description.substring(0, pos);
111:                        while (pos < description.length()
112:                                && UCharacterProperty
113:                                        .isRuleWhiteSpace(description
114:                                                .charAt(++pos))) {
115:                        }
116:                        description = description.substring(pos);
117:                        descriptions[index] = description;
118:                    }
119:
120:                    // if the description doesn't begin with a rule set name, its
121:                    // name is "%default"
122:                } else {
123:                    name = "%default";
124:                }
125:
126:                if (description.length() == 0) {
127:                    throw new IllegalArgumentException(
128:                            "Empty rule set description");
129:                }
130:
131:                // all of the other members of NFRuleSet are initialized
132:                // by parseRules()
133:            }
134:
135:            /**
136:             * Construct the subordinate data structures used by this object.
137:             * This function is called by the RuleBasedNumberFormat constructor
138:             * after all the rule sets have been created to actually parse
139:             * the description and build rules from it.  Since any rule set
140:             * can refer to any other rule set, we have to have created all of
141:             * them before we can create anything else.
142:             * @param description The textual description of this rule set
143:             * @param owner The formatter that owns this rule set
144:             */
145:            public void parseRules(String description,
146:                    RuleBasedNumberFormat owner) {
147:                // start by creating a Vector whose elements are Strings containing
148:                // the descriptions of the rules (one rule per element).  The rules
149:                // are separated by semicolons (there's no escape facility: ALL
150:                // semicolons are rule delimiters)
151:                Vector ruleDescriptions = new Vector();
152:
153:                int oldP = 0;
154:                int p = description.indexOf(';');
155:                while (oldP != -1) {
156:                    if (p != -1) {
157:                        ruleDescriptions.addElement(description.substring(oldP,
158:                                p));
159:                        oldP = p + 1;
160:                    } else {
161:                        if (oldP < description.length()) {
162:                            ruleDescriptions.addElement(description
163:                                    .substring(oldP));
164:                        }
165:                        oldP = p;
166:                    }
167:                    p = description.indexOf(';', p + 1);
168:                }
169:
170:                // now go back through and build a vector of the rules themselves
171:                // (the number of elements in the description list isn't necessarily
172:                // the number of rules-- some descriptions may expend into two rules)
173:                Vector tempRules = new Vector();
174:
175:                // we keep track of the rule before the one we're currently working
176:                // on solely to support >>> substitutions
177:                NFRule predecessor = null;
178:                for (int i = 0; i < ruleDescriptions.size(); i++) {
179:                    // makeRules (a factory method on NFRule) will return either
180:                    // a single rule or an array of rules.  Either way, add them
181:                    // to our rule vector
182:                    Object temp = NFRule.makeRules((String) ruleDescriptions
183:                            .elementAt(i), this , predecessor, owner);
184:
185:                    if (temp instanceof  NFRule) {
186:                        tempRules.addElement(temp);
187:                        predecessor = (NFRule) temp;
188:                    } else if (temp instanceof  NFRule[]) {
189:                        NFRule[] rulesToAdd = (NFRule[]) temp;
190:
191:                        for (int j = 0; j < rulesToAdd.length; j++) {
192:                            tempRules.addElement(rulesToAdd[j]);
193:                            predecessor = rulesToAdd[j];
194:                        }
195:                    }
196:                }
197:                // now we can bag the description list
198:                ruleDescriptions = null;
199:
200:                // for rules that didn't specify a base value, their base values
201:                // were initialized to 0.  Make another pass through the list and
202:                // set all those rules' base values.  We also remove any special
203:                // rules from the list and put them into their own member variables
204:                long defaultBaseValue = 0;
205:
206:                // (this isn't a for loop because we might be deleting items from
207:                // the vector-- we want to make sure we only increment i when
208:                // we _didn't_ delete aything from the vector)
209:                int i = 0;
210:                while (i < tempRules.size()) {
211:                    NFRule rule = (NFRule) tempRules.elementAt(i);
212:
213:                    switch ((int) rule.getBaseValue()) {
214:                    // if the rule's base value is 0, fill in a default
215:                    // base value (this will be 1 plus the preceding
216:                    // rule's base value for regular rule sets, and the
217:                    // same as the preceding rule's base value in fraction
218:                    // rule sets)
219:                    case 0:
220:                        rule.setBaseValue(defaultBaseValue);
221:                        if (!isFractionRuleSet) {
222:                            ++defaultBaseValue;
223:                        }
224:                        ++i;
225:                        break;
226:
227:                    // if it's the negative-number rule, copy it into its own
228:                    // data member and delete it from the list
229:                    case NFRule.NEGATIVE_NUMBER_RULE:
230:                        negativeNumberRule = rule;
231:                        tempRules.removeElementAt(i);
232:                        break;
233:
234:                    // if it's the improper fraction rule, copy it into the
235:                    // correct element of fractionRules
236:                    case NFRule.IMPROPER_FRACTION_RULE:
237:                        fractionRules[0] = rule;
238:                        tempRules.removeElementAt(i);
239:                        break;
240:
241:                    // if it's the proper fraction rule, copy it into the
242:                    // correct element of fractionRules
243:                    case NFRule.PROPER_FRACTION_RULE:
244:                        fractionRules[1] = rule;
245:                        tempRules.removeElementAt(i);
246:                        break;
247:
248:                    // if it's the master rule, copy it into the
249:                    // correct element of fractionRules
250:                    case NFRule.MASTER_RULE:
251:                        fractionRules[2] = rule;
252:                        tempRules.removeElementAt(i);
253:                        break;
254:
255:                    // if it's a regular rule that already knows its base value,
256:                    // check to make sure the rules are in order, and update
257:                    // the default base value for the next rule
258:                    default:
259:                        if (rule.getBaseValue() < defaultBaseValue) {
260:                            throw new IllegalArgumentException(
261:                                    "Rules are not in order, base: "
262:                                            + rule.getBaseValue() + " < "
263:                                            + defaultBaseValue);
264:                        }
265:                        defaultBaseValue = rule.getBaseValue();
266:                        if (!isFractionRuleSet) {
267:                            ++defaultBaseValue;
268:                        }
269:                        ++i;
270:                        break;
271:                    }
272:                }
273:
274:                // finally, we can copy the rules from the vector into a
275:                // fixed-length array
276:                rules = new NFRule[tempRules.size()];
277:                tempRules.copyInto((Object[]) rules);
278:            }
279:
280:            /**
281:             * Flags this rule set as a fraction rule set.  This function is
282:             * called during the construction process once we know this rule
283:             * set is a fraction rule set.  We don't know a rule set is a
284:             * fraction rule set until we see it used somewhere.  This function
285:             * is not ad must not be called at any time other than during
286:             * construction of a RuleBasedNumberFormat.
287:             */
288:            public void makeIntoFractionRuleSet() {
289:                isFractionRuleSet = true;
290:            }
291:
292:            //-----------------------------------------------------------------------
293:            // boilerplate
294:            //-----------------------------------------------------------------------
295:
296:            /**
297:             * Compares two rule sets for equality.
298:             * @param that The other rule set
299:             * @return true if the two rule sets are functionally equivalent.
300:             */
301:            public boolean equals(Object that) {
302:                // if different classes, they're not equal
303:                if (!(that instanceof  NFRuleSet)) {
304:                    return false;
305:                } else {
306:                    // otherwise, compare the members one by one...
307:                    NFRuleSet that2 = (NFRuleSet) that;
308:
309:                    if (!name.equals(that2.name)
310:                            || !Utility.objectEquals(negativeNumberRule,
311:                                    that2.negativeNumberRule)
312:                            || !Utility.objectEquals(fractionRules[0],
313:                                    that2.fractionRules[0])
314:                            || !Utility.objectEquals(fractionRules[1],
315:                                    that2.fractionRules[1])
316:                            || !Utility.objectEquals(fractionRules[2],
317:                                    that2.fractionRules[2])
318:                            || rules.length != that2.rules.length
319:                            || isFractionRuleSet != that2.isFractionRuleSet) {
320:
321:                        return false;
322:                    }
323:
324:                    // ...then compare the rule lists...
325:                    for (int i = 0; i < rules.length; i++) {
326:                        if (!rules[i].equals(that2.rules[i])) {
327:                            return false;
328:                        }
329:                    }
330:
331:                    // ...and if we make it here, tney're equal
332:                    return true;
333:                }
334:            }
335:
336:            /**
337:             * Builds a textual representation of a rule set.
338:             * @return A textual representation of a rule set.  This won't
339:             * necessarily be the same description that the rule set was
340:             * constructed with, but it will produce the same results.
341:             */
342:            public String toString() {
343:                StringBuffer result = new StringBuffer();
344:
345:                // the rule set name goes first...
346:                result.append(name + ":\n");
347:
348:                // followed by the regular rules...
349:                for (int i = 0; i < rules.length; i++) {
350:                    result.append("    " + rules[i].toString() + "\n");
351:                }
352:
353:                // followed by the special rules (if they exist)
354:                if (negativeNumberRule != null) {
355:                    result
356:                            .append("    " + negativeNumberRule.toString()
357:                                    + "\n");
358:                }
359:                if (fractionRules[0] != null) {
360:                    result.append("    " + fractionRules[0].toString() + "\n");
361:                }
362:                if (fractionRules[1] != null) {
363:                    result.append("    " + fractionRules[1].toString() + "\n");
364:                }
365:                if (fractionRules[2] != null) {
366:                    result.append("    " + fractionRules[2].toString() + "\n");
367:                }
368:
369:                return result.toString();
370:            }
371:
372:            //-----------------------------------------------------------------------
373:            // simple accessors
374:            //-----------------------------------------------------------------------
375:
376:            /**
377:             * Says whether this rule set is a fraction rule set.
378:             * @return true if this rule is a fraction rule set; false if it isn't
379:             */
380:            public boolean isFractionSet() {
381:                return isFractionRuleSet;
382:            }
383:
384:            /**
385:             * Returns the rule set's name
386:             * @return The rule set's name
387:             */
388:            public String getName() {
389:                return name;
390:            }
391:
392:            /**
393:             * Return true if the rule set is public.
394:             * @return true if the rule set is public
395:             */
396:            public boolean isPublic() {
397:                return !name.startsWith("%%");
398:            }
399:
400:            //-----------------------------------------------------------------------
401:            // formatting
402:            //-----------------------------------------------------------------------
403:
404:            /**
405:             * Formats a long.  Selects an appropriate rule and dispatches
406:             * control to it.
407:             * @param number The number being formatted
408:             * @param toInsertInto The string where the result is to be placed
409:             * @param pos The position in toInsertInto where the result of
410:             * this operation is to be inserted
411:             */
412:            public void format(long number, StringBuffer toInsertInto, int pos) {
413:                NFRule applicableRule = findNormalRule(number);
414:
415:                if (++recursionCount >= RECURSION_LIMIT) {
416:                    recursionCount = 0;
417:                    throw new IllegalStateException(
418:                            "Recursion limit exceeded when applying ruleSet "
419:                                    + name);
420:                }
421:                applicableRule.doFormat(number, toInsertInto, pos);
422:                --recursionCount;
423:            }
424:
425:            /**
426:             * Formats a double.  Selects an appropriate rule and dispatches
427:             * control to it.
428:             * @param number The number being formatted
429:             * @param toInsertInto The string where the result is to be placed
430:             * @param pos The position in toInsertInto where the result of
431:             * this operation is to be inserted
432:             */
433:            public void format(double number, StringBuffer toInsertInto, int pos) {
434:                NFRule applicableRule = findRule(number);
435:
436:                if (++recursionCount >= RECURSION_LIMIT) {
437:                    recursionCount = 0;
438:                    throw new IllegalStateException(
439:                            "Recursion limit exceeded when applying ruleSet "
440:                                    + name);
441:                }
442:                applicableRule.doFormat(number, toInsertInto, pos);
443:                --recursionCount;
444:            }
445:
446:            /**
447:             * Selects an apropriate rule for formatting the number.
448:             * @param number The number being formatted.
449:             * @return The rule that should be used to format it
450:             */
451:            private NFRule findRule(double number) {
452:                // if this is a fraction rule set, use findFractionRuleSetRule()
453:                if (isFractionRuleSet) {
454:                    return findFractionRuleSetRule(number);
455:                }
456:
457:                // if the number is negative, return the negative number rule
458:                // (if there isn't a negative-number rule, we pretend it's a
459:                // positive number)
460:                if (number < 0) {
461:                    if (negativeNumberRule != null) {
462:                        return negativeNumberRule;
463:                    } else {
464:                        number = -number;
465:                    }
466:                }
467:
468:                // if the number isn't an integer, we use one f the fraction rules...
469:                if (number != Math.floor(number)) {
470:                    // if the number is between 0 and 1, return the proper
471:                    // fraction rule
472:                    if (number < 1 && fractionRules[1] != null) {
473:                        return fractionRules[1];
474:                    }
475:
476:                    // otherwise, return the improper fraction rule
477:                    else if (fractionRules[0] != null) {
478:                        return fractionRules[0];
479:                    }
480:                }
481:
482:                // if there's a master rule, use it to format the number
483:                if (fractionRules[2] != null) {
484:                    return fractionRules[2];
485:
486:                    // and if we haven't yet returned a rule, use findNormalRule()
487:                    // to find the applicable rule
488:                } else {
489:                    return findNormalRule((long) Math.round(number));
490:                }
491:            }
492:
493:            /**
494:             * If the value passed to findRule() is a positive integer, findRule()
495:             * uses this function to select the appropriate rule.  The result will
496:             * generally be the rule with the highest base value less than or equal
497:             * to the number.  There is one exception to this: If that rule has
498:             * two substitutions and a base value that is not an even multiple of
499:             * its divisor, and the number itself IS an even multiple of the rule's
500:             * divisor, then the result will be the rule that preceded the original
501:             * result in the rule list.  (This behavior is known as the "rollback
502:             * rule", and is used to handle optional text: a rule with optional
503:             * text is represented internally as two rules, and the rollback rule
504:             * selects appropriate between them.  This avoids things like "two
505:             * hundred zero".)
506:             * @param number The number being formatted
507:             * @return The rule to use to format this number
508:             */
509:            private NFRule findNormalRule(long number) {
510:                // if this is a fraction rule set, use findFractionRuleSetRule()
511:                // to find the rule (we should only go into this clause if the
512:                // value is 0)
513:                if (isFractionRuleSet) {
514:                    return findFractionRuleSetRule(number);
515:                }
516:
517:                // if the number is negative, return the negative-number rule
518:                // (if there isn't one, pretend the number is positive)
519:                if (number < 0) {
520:                    if (negativeNumberRule != null) {
521:                        return negativeNumberRule;
522:                    } else {
523:                        number = -number;
524:                    }
525:                }
526:
527:                // we have to repeat the preceding two checks, even though we
528:                // do them in findRule(), because the version of format() that
529:                // takes a long bypasses findRule() and goes straight to this
530:                // function.  This function does skip the fraction rules since
531:                // we know the value is an integer (it also skips the master
532:                // rule, since it's considered a fraction rule.  Skipping the
533:                // master rule in this function is also how we avoid infinite
534:                // recursion)
535:
536:                // binary-search the rule list for the applicable rule
537:                // (a rule is used for all values from its base value to
538:                // the next rule's base value)
539:                int lo = 0;
540:                int hi = rules.length;
541:                if (hi > 0) {
542:                    while (lo < hi) {
543:                        int mid = (lo + hi) / 2;
544:                        if (rules[mid].getBaseValue() == number) {
545:                            return rules[mid];
546:                        } else if (rules[mid].getBaseValue() > number) {
547:                            hi = mid;
548:                        } else {
549:                            lo = mid + 1;
550:                        }
551:                    }
552:                    if (hi == 0) { // bad rule set
553:                        throw new IllegalStateException("The rule set " + name
554:                                + " cannot format the value " + number);
555:                    }
556:                    NFRule result = rules[hi - 1];
557:
558:                    // use shouldRollBack() to see whether we need to invoke the
559:                    // rollback rule (see shouldRollBack()'s documentation for
560:                    // an explanation of the rollback rule).  If we do, roll back
561:                    // one rule and return that one instead of the one we'd normally
562:                    // return
563:                    if (result.shouldRollBack(number)) {
564:                        if (hi == 1) { // bad rule set
565:                            throw new IllegalStateException("The rule set "
566:                                    + name
567:                                    + " cannot roll back from the rule '"
568:                                    + result + "'");
569:                        }
570:                        result = rules[hi - 2];
571:                    }
572:                    return result;
573:                }
574:                // else use the master rule
575:                return fractionRules[2];
576:            }
577:
578:            /**
579:             * If this rule is a fraction rule set, this function is used by
580:             * findRule() to select the most appropriate rule for formatting
581:             * the number.  Basically, the base value of each rule in the rule
582:             * set is treated as the denominator of a fraction.  Whichever
583:             * denominator can produce the fraction closest in value to the
584:             * number passed in is the result.  If there's a tie, the earlier
585:             * one in the list wins.  (If there are two rules in a row with the
586:             * same base value, the first one is used when the numerator of the
587:             * fraction would be 1, and the second rule is used the rest of the
588:             * time.
589:             * @param number The number being formatted (which will always be
590:             * a number between 0 and 1)
591:             * @return The rule to use to format this number
592:             */
593:            private NFRule findFractionRuleSetRule(double number) {
594:                // the obvious way to do this (multiply the value being formatted
595:                // by each rule's base value until you get an integral result)
596:                // doesn't work because of rounding error.  This method is more
597:                // accurate
598:
599:                // find the least common multiple of the rules' base values
600:                // and multiply this by the number being formatted.  This is
601:                // all the precision we need, and we can do all of the rest
602:                // of the math using integer arithmetic
603:                long leastCommonMultiple = rules[0].getBaseValue();
604:                for (int i = 1; i < rules.length; i++) {
605:                    leastCommonMultiple = lcm(leastCommonMultiple, rules[i]
606:                            .getBaseValue());
607:                }
608:                long numerator = (long) (Math.round(number
609:                        * leastCommonMultiple));
610:
611:                // for each rule, do the following...
612:                long tempDifference;
613:                long difference = Long.MAX_VALUE;
614:                int winner = 0;
615:                for (int i = 0; i < rules.length; i++) {
616:                    // "numerator" is the numerator of the fraction is the
617:                    // denominator is the LCD.  The numerator if the the rule's
618:                    // base value is the denomiator is "numerator" times the
619:                    // base value divided bythe LCD.  Here we check to see if
620:                    // that's an integer, and if not, how close it is to being
621:                    // an integer.
622:                    tempDifference = numerator * rules[i].getBaseValue()
623:                            % leastCommonMultiple;
624:
625:                    // normalize the result of the above calculation: we want
626:                    // the numerator's distance from the CLOSEST multiple
627:                    // of the LCD
628:                    if (leastCommonMultiple - tempDifference < tempDifference) {
629:                        tempDifference = leastCommonMultiple - tempDifference;
630:                    }
631:
632:                    // if this is as close as we've come, keep track of how close
633:                    // that is, and the line number of the rule that did it.  If
634:                    // we've scored a direct hit, we don't have to look at any more
635:                    // rules
636:                    if (tempDifference < difference) {
637:                        difference = tempDifference;
638:                        winner = i;
639:                        if (difference == 0) {
640:                            break;
641:                        }
642:                    }
643:                }
644:
645:                // if we have two successive rules that both have the winning base
646:                // value, then the first one (the one we found above) is used if
647:                // the numerator of the fraction is 1 and the second one is used if
648:                // the numerator of the fraction is anything else (this lets us
649:                // do things like "one third"/"two thirds" without haveing to define
650:                // a whole bunch of extra rule sets)
651:                if (winner + 1 < rules.length
652:                        && rules[winner + 1].getBaseValue() == rules[winner]
653:                                .getBaseValue()) {
654:                    if (Math.round(number * rules[winner].getBaseValue()) < 1
655:                            || Math
656:                                    .round(number
657:                                            * rules[winner].getBaseValue()) >= 2) {
658:                        ++winner;
659:                    }
660:                }
661:
662:                // finally, return the winning rule
663:                return rules[winner];
664:            }
665:
666:            /**
667:             * Calculates the least common multiple of x and y.
668:             */
669:            private static long lcm(long x, long y) {
670:                // binary gcd algorithm from Knuth, "The Art of Computer Programming,"
671:                // vol. 2, 1st ed., pp. 298-299
672:                long x1 = x;
673:                long y1 = y;
674:
675:                int p2 = 0;
676:                while ((x1 & 1) == 0 && (y1 & 1) == 0) {
677:                    ++p2;
678:                    x1 >>= 1;
679:                    y1 >>= 1;
680:                }
681:
682:                long t;
683:                if ((x1 & 1) == 1) {
684:                    t = -y1;
685:                } else {
686:                    t = x1;
687:                }
688:
689:                while (t != 0) {
690:                    while ((t & 1) == 0) {
691:                        t >>= 1;
692:                    }
693:                    if (t > 0) {
694:                        x1 = t;
695:                    } else {
696:                        y1 = -t;
697:                    }
698:                    t = x1 - y1;
699:                }
700:                long gcd = x1 << p2;
701:
702:                // x * y == gcd(x, y) * lcm(x, y)
703:                return x / gcd * y;
704:            }
705:
706:            //-----------------------------------------------------------------------
707:            // parsing
708:            //-----------------------------------------------------------------------
709:
710:            /**
711:             * Parses a string.  Matches the string to be parsed against each
712:             * of its rules (with a base value less than upperBound) and returns
713:             * the value produced by the rule that matched the most charcters
714:             * in the source string.
715:             * @param text The string to parse
716:             * @param parsePosition The initial position is ignored and assumed
717:             * to be 0.  On exit, this object has been updated to point to the
718:             * first character position this rule set didn't consume.
719:             * @param upperBound Limits the rules that can be allowed to match.
720:             * Only rules whose base values are strictly less than upperBound
721:             * are considered.
722:             * @return The numerical result of parsing this string.  This will
723:             * be the matching rule's base value, composed appropriately with
724:             * the results of matching any of its substitutions.  The object
725:             * will be an instance of Long if it's an integral value; otherwise,
726:             * it will be an instance of Double.  This function always returns
727:             * a valid object: If nothing matched the input string at all,
728:             * this function returns new Long(0), and the parse position is
729:             * left unchanged.
730:             */
731:            public Number parse(String text, ParsePosition parsePosition,
732:                    double upperBound) {
733:                // try matching each rule in the rule set against the text being
734:                // parsed.  Whichever one matches the most characters is the one
735:                // that determines the value we return.
736:
737:                ParsePosition highWaterMark = new ParsePosition(0);
738:                Number result = new Long(0);
739:                Number tempResult = null;
740:
741:                // dump out if there's no text to parse
742:                if (text.length() == 0) {
743:                    return result;
744:                }
745:
746:                // start by trying the nehative number rule (if there is one)
747:                if (negativeNumberRule != null) {
748:                    tempResult = negativeNumberRule.doParse(text,
749:                            parsePosition, false, upperBound);
750:                    if (parsePosition.getIndex() > highWaterMark.getIndex()) {
751:                        result = tempResult;
752:                        highWaterMark.setIndex(parsePosition.getIndex());
753:                    }
754:                    // commented out because the error-index API on ParsePosition isn't there in 1.1.x
755:                    //            if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
756:                    //                highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
757:                    //            }
758:                    parsePosition.setIndex(0);
759:                }
760:
761:                // then try each of the fraction rules
762:                for (int i = 0; i < 3; i++) {
763:                    if (fractionRules[i] != null) {
764:                        tempResult = fractionRules[i].doParse(text,
765:                                parsePosition, false, upperBound);
766:                        if (parsePosition.getIndex() > highWaterMark.getIndex()) {
767:                            result = tempResult;
768:                            highWaterMark.setIndex(parsePosition.getIndex());
769:                        }
770:                        // commented out because the error-index API on ParsePosition isn't there in 1.1.x
771:                        //            if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
772:                        //                highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
773:                        //            }
774:                        parsePosition.setIndex(0);
775:                    }
776:                }
777:
778:                // finally, go through the regular rules one at a time.  We start
779:                // at the end of the list because we want to try matching the most
780:                // sigificant rule first (this helps ensure that we parse
781:                // "five thousand three hundred six" as
782:                // "(five thousand) (three hundred) (six)" rather than
783:                // "((five thousand three) hundred) (six)").  Skip rules whose
784:                // base values are higher than the upper bound (again, this helps
785:                // limit ambiguity by making sure the rules that match a rule's
786:                // are less significant than the rule containing the substitutions)/
787:                for (int i = rules.length - 1; i >= 0
788:                        && highWaterMark.getIndex() < text.length(); i--) {
789:                    if (!isFractionRuleSet
790:                            && rules[i].getBaseValue() >= upperBound) {
791:                        continue;
792:                    }
793:
794:                    tempResult = rules[i].doParse(text, parsePosition,
795:                            isFractionRuleSet, upperBound);
796:                    if (parsePosition.getIndex() > highWaterMark.getIndex()) {
797:                        result = tempResult;
798:                        highWaterMark.setIndex(parsePosition.getIndex());
799:                    }
800:                    // commented out because the error-index API on ParsePosition isn't there in 1.1.x
801:                    //            if (parsePosition.getErrorIndex() > highWaterMark.getErrorIndex()) {
802:                    //                highWaterMark.setErrorIndex(parsePosition.getErrorIndex());
803:                    //            }
804:                    parsePosition.setIndex(0);
805:                }
806:
807:                // finally, update the parse postion we were passed to point to the
808:                // first character we didn't use, and return the result that
809:                // cporresponds to that string of characters
810:                parsePosition.setIndex(highWaterMark.getIndex());
811:                // commented out because the error-index API on ParsePosition isn't there in 1.1.x
812:                //        if (parsePosition.getIndex() == 0) {
813:                //            parsePosition.setErrorIndex(highWaterMark.getErrorIndex());
814:                //        }
815:
816:                return result;
817:            }
818:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.