0001: /*
0002: *******************************************************************************
0003: * Copyright (C) 1996-2004, International Business Machines Corporation and *
0004: * others. All Rights Reserved. *
0005: *******************************************************************************
0006: */
0007: package com.ibm.icu.text;
0008:
0009: import java.text.*;
0010:
0011: //===================================================================
0012: // NFSubstitution (abstract base class)
0013: //===================================================================
0014:
0015: /**
0016: * An abstract class defining protocol for substitutions. A substitution
0017: * is a section of a rule that inserts text into the rule's rule text
0018: * based on some part of the number being formatted.
0019: * @author Richard Gillam
0020: */
0021: abstract class NFSubstitution {
0022: //-----------------------------------------------------------------------
0023: // constants
0024: //-----------------------------------------------------------------------
0025:
0026: /**
0027: * Puts a copyright in the .class file
0028: */
0029: private static final String copyrightNotice = "Copyright \u00a91997-2004 IBM Corp. All rights reserved.";
0030:
0031: //-----------------------------------------------------------------------
0032: // data members
0033: //-----------------------------------------------------------------------
0034:
0035: /**
0036: * The substitution's position in the rule text of the rule that owns it
0037: */
0038: int pos;
0039:
0040: /**
0041: * The rule set this substitution uses to format its result, or null.
0042: * (Either this or numberFormat has to be non-null.)
0043: */
0044: NFRuleSet ruleSet = null;
0045:
0046: /**
0047: * The DecimalFormat this substitution uses to format its result,
0048: * or null. (Either this or ruleSet has to be non-null.)
0049: */
0050: DecimalFormat numberFormat = null;
0051:
0052: //-----------------------------------------------------------------------
0053: // construction
0054: //-----------------------------------------------------------------------
0055:
0056: /**
0057: * Parses the description, creates the right kind of substitution,
0058: * and initializes it based on the description.
0059: * @param pos The substitution's position in the rule text of the
0060: * rule that owns it.
0061: * @param rule The rule containing this substitution
0062: * @param rulePredecessor The rule preceding the one that contains
0063: * this substitution in the rule set's rule list (this is used
0064: * only for >>> substitutions).
0065: * @param ruleSet The rule set containing the rule containing this
0066: * substitution
0067: * @param formatter The RuleBasedNumberFormat that ultimately owns
0068: * this substitution
0069: * @param description The description to parse to build the substitution
0070: * (this is just the substring of the rule's description containing
0071: * the substitution token itself)
0072: * @return A new substitution constructed according to the description
0073: */
0074: public static NFSubstitution makeSubstitution(int pos, NFRule rule,
0075: NFRule rulePredecessor, NFRuleSet ruleSet,
0076: RuleBasedNumberFormat formatter, String description) {
0077: // if the description is empty, return a NummSubstitution
0078: if (description.length() == 0) {
0079: return new NullSubstitution(pos, ruleSet, formatter,
0080: description);
0081: }
0082:
0083: switch (description.charAt(0)) {
0084: // if the description begins with '<'...
0085: case '<':
0086: // throw an exception if the rule is a negative number
0087: // rule
0088: if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {
0089: throw new IllegalArgumentException(
0090: "<< not allowed in negative-number rule");
0091: }
0092:
0093: // if the rule is a fraction rule, return an
0094: // IntegralPartSubstitution
0095: else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
0096: || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
0097: || rule.getBaseValue() == NFRule.MASTER_RULE) {
0098: return new IntegralPartSubstitution(pos, ruleSet,
0099: formatter, description);
0100: }
0101:
0102: // if the rule set containing the rule is a fraction
0103: // rule set, return a NumeratorSubstitution
0104: else if (ruleSet.isFractionSet()) {
0105: return new NumeratorSubstitution(pos, rule
0106: .getBaseValue(), formatter.getDefaultRuleSet(),
0107: formatter, description);
0108: }
0109:
0110: // otherwise, return a MultiplierSubstitution
0111: else {
0112: return new MultiplierSubstitution(pos, rule
0113: .getDivisor(), ruleSet, formatter, description);
0114: }
0115:
0116: // if the description begins with '>'...
0117: case '>':
0118: // if the rule is a negative-number rule, return
0119: // an AbsoluteValueSubstitution
0120: if (rule.getBaseValue() == NFRule.NEGATIVE_NUMBER_RULE) {
0121: return new AbsoluteValueSubstitution(pos, ruleSet,
0122: formatter, description);
0123: }
0124:
0125: // if the rule is a fraction rule, return a
0126: // FractionalPartSubstitution
0127: else if (rule.getBaseValue() == NFRule.IMPROPER_FRACTION_RULE
0128: || rule.getBaseValue() == NFRule.PROPER_FRACTION_RULE
0129: || rule.getBaseValue() == NFRule.MASTER_RULE) {
0130: return new FractionalPartSubstitution(pos, ruleSet,
0131: formatter, description);
0132: }
0133:
0134: // if the rule set owning the rule is a fraction rule set,
0135: // throw an exception
0136: else if (ruleSet.isFractionSet()) {
0137: throw new IllegalArgumentException(
0138: ">> not allowed in fraction rule set");
0139: }
0140:
0141: // otherwise, return a ModulusSubstitution
0142: else {
0143: return new ModulusSubstitution(pos, rule.getDivisor(),
0144: rulePredecessor, ruleSet, formatter,
0145: description);
0146: }
0147:
0148: // if the description begins with '=', always return a
0149: // SameValueSubstitution
0150: case '=':
0151: return new SameValueSubstitution(pos, ruleSet, formatter,
0152: description);
0153:
0154: // and if it's anything else, throw an exception
0155: default:
0156: throw new IllegalArgumentException(
0157: "Illegal substitution character");
0158: }
0159: }
0160:
0161: /**
0162: * Base constructor for substitutions. This constructor sets up the
0163: * fields which are common to all substitutions.
0164: * @param pos The substitution's position in the owning rule's rule
0165: * text
0166: * @param ruleSet The rule set that owns this substitution
0167: * @param formatter The RuleBasedNumberFormat that owns this substitution
0168: * @param description The substitution descriptor (i.e., the text
0169: * inside the token characters)
0170: */
0171: NFSubstitution(int pos, NFRuleSet ruleSet,
0172: RuleBasedNumberFormat formatter, String description) {
0173: // initialize the substitution's position in its parent rule
0174: this .pos = pos;
0175:
0176: // the description should begin and end with the same character.
0177: // If it doesn't that's a syntax error. Otherwise,
0178: // makeSubstitution() was the only thing that needed to know
0179: // about these characters, so strip them off
0180: if (description.length() >= 2
0181: && description.charAt(0) == description
0182: .charAt(description.length() - 1)) {
0183: description = description.substring(1,
0184: description.length() - 1);
0185: } else if (description.length() != 0) {
0186: throw new IllegalArgumentException(
0187: "Illegal substitution syntax");
0188: }
0189:
0190: // if the description was just two paired token characters
0191: // (i.e., "<<" or ">>"), it uses the rule set it belongs to to
0192: // format its result
0193: if (description.length() == 0) {
0194: this .ruleSet = ruleSet;
0195: }
0196:
0197: // if the description contains a rule set name, that's the rule
0198: // set we use to format the result: get a reference to the
0199: // names rule set
0200: else if (description.charAt(0) == '%') {
0201: this .ruleSet = formatter.findRuleSet(description);
0202: }
0203:
0204: // if the description begins with 0 or #, treat it as a
0205: // DecimalFormat pattern, and initialize a DecimalFormat with
0206: // that pattern (then set it to use the DecimalFormatSymbols
0207: // belonging to our formatter)
0208: else if (description.charAt(0) == '#'
0209: || description.charAt(0) == '0') {
0210: this .numberFormat = new DecimalFormat(description);
0211: this .numberFormat.setDecimalFormatSymbols(formatter
0212: .getDecimalFormatSymbols());
0213: }
0214:
0215: // if the description is ">>>", this substitution bypasses the
0216: // usual rule-search process and always uses the rule that precedes
0217: // it in its own rule set's rule list (this is used for place-value
0218: // notations: formats where you want to see a particular part of
0219: // a number even when it's 0)
0220: else if (description.charAt(0) == '>') {
0221: this .ruleSet = ruleSet; // was null, thai rules added to control space
0222: this .numberFormat = null;
0223: }
0224:
0225: // and of the description is none of these things, it's a syntax error
0226: else {
0227: throw new IllegalArgumentException(
0228: "Illegal substitution syntax");
0229: }
0230: }
0231:
0232: /**
0233: * Set's the substitution's divisor. Used by NFRule.setBaseValue().
0234: * A no-op for all substitutions except multiplier and modulus
0235: * substitutions.
0236: * @param radix The radix of the divisor
0237: * @param exponent The exponent of the divisor
0238: */
0239: public void setDivisor(int radix, int exponent) {
0240: // a no-op for all substitutions except multiplier and modulus substitutions
0241: }
0242:
0243: //-----------------------------------------------------------------------
0244: // boilerplate
0245: //-----------------------------------------------------------------------
0246:
0247: /**
0248: * Compares two substitutions for equality
0249: * @param The substitution to compare this one to
0250: * @return true if the two substitutions are functionally equivalent
0251: */
0252: public boolean equals(Object that) {
0253: // compare class and all of the fields all substitutions have
0254: // in common
0255: if (this .getClass() == that.getClass()) {
0256: NFSubstitution that2 = (NFSubstitution) that;
0257:
0258: return pos == that2.pos
0259: && (ruleSet == null ? that2.ruleSet == null : true) // can't compare tree structure, no .equals or recurse
0260: && (numberFormat == null ? (that2.numberFormat == null)
0261: : numberFormat.equals(that2.numberFormat));
0262: }
0263: return false;
0264: }
0265:
0266: /**
0267: * Returns a textual description of the substitution
0268: * @return A textual description of the substitution. This might
0269: * not be identical to the description it was created from, but
0270: * it'll produce the same result.
0271: */
0272: public String toString() {
0273: // use tokenChar() to get the character at the beginning and
0274: // end of the substitution token. In between them will go
0275: // either the name of the rule set it uses, or the pattern of
0276: // the DecimalFormat it uses
0277: if (ruleSet != null) {
0278: return tokenChar() + ruleSet.getName() + tokenChar();
0279: } else {
0280: return tokenChar() + numberFormat.toPattern() + tokenChar();
0281: }
0282: }
0283:
0284: //-----------------------------------------------------------------------
0285: // formatting
0286: //-----------------------------------------------------------------------
0287:
0288: /**
0289: * Performs a mathematical operation on the number, formats it using
0290: * either ruleSet or decimalFormat, and inserts the result into
0291: * toInsertInto.
0292: * @param number The number being formatted.
0293: * @param toInsertInto The string we insert the result into
0294: * @param pos The position in toInsertInto where the owning rule's
0295: * rule text begins (this value is added to this substitution's
0296: * position to determine exactly where to insert the new text)
0297: */
0298: public void doSubstitution(long number, StringBuffer toInsertInto,
0299: int pos) {
0300: if (ruleSet != null) {
0301: // perform a transformation on the number that is dependent
0302: // on the type of substitution this is, then just call its
0303: // rule set's format() method to format the result
0304: long numberToFormat = transformNumber(number);
0305:
0306: ruleSet
0307: .format(numberToFormat, toInsertInto, pos
0308: + this .pos);
0309: } else {
0310: // or perform the transformation on the number (preserving
0311: // the result's fractional part if the formatter it set
0312: // to show it), then use that formatter's format() method
0313: // to format the result
0314: double numberToFormat = transformNumber((double) number);
0315: if (numberFormat.getMaximumFractionDigits() == 0) {
0316: numberToFormat = Math.floor(numberToFormat);
0317: }
0318:
0319: toInsertInto.insert(pos + this .pos, numberFormat
0320: .format(numberToFormat));
0321: }
0322: }
0323:
0324: /**
0325: * Performs a mathematical operation on the number, formats it using
0326: * either ruleSet or decimalFormat, and inserts the result into
0327: * toInsertInto.
0328: * @param number The number being formatted.
0329: * @param toInsertInto The string we insert the result into
0330: * @param pos The position in toInsertInto where the owning rule's
0331: * rule text begins (this value is added to this substitution's
0332: * position to determine exactly where to insert the new text)
0333: */
0334: public void doSubstitution(double number,
0335: StringBuffer toInsertInto, int pos) {
0336: // perform a transformation on the number being formatted that
0337: // is dependent on the type of substitution this is
0338: double numberToFormat = transformNumber(number);
0339:
0340: // if the result is an integer, from here on out we work in integer
0341: // space (saving time and memory and preserving accuracy)
0342: if (numberToFormat == Math.floor(numberToFormat)
0343: && ruleSet != null) {
0344: ruleSet.format((long) numberToFormat, toInsertInto, pos
0345: + this .pos);
0346:
0347: // if the result isn't an integer, then call either our rule set's
0348: // format() method or our DecimalFormat's format() method to
0349: // format the result
0350: } else {
0351: if (ruleSet != null) {
0352: ruleSet.format(numberToFormat, toInsertInto, pos
0353: + this .pos);
0354: } else {
0355: toInsertInto.insert(pos + this .pos, numberFormat
0356: .format(numberToFormat));
0357: }
0358: }
0359: }
0360:
0361: /**
0362: * Subclasses override this function to perform some kind of
0363: * mathematical operation on the number. The result of this operation
0364: * is formatted using the rule set or DecimalFormat that this
0365: * substitution refers to, and the result is inserted into the result
0366: * string.
0367: * @param The number being formatted
0368: * @return The result of performing the opreration on the number
0369: */
0370: public abstract long transformNumber(long number);
0371:
0372: /**
0373: * Subclasses override this function to perform some kind of
0374: * mathematical operation on the number. The result of this operation
0375: * is formatted using the rule set or DecimalFormat that this
0376: * substitution refers to, and the result is inserted into the result
0377: * string.
0378: * @param The number being formatted
0379: * @return The result of performing the opreration on the number
0380: */
0381: public abstract double transformNumber(double number);
0382:
0383: //-----------------------------------------------------------------------
0384: // parsing
0385: //-----------------------------------------------------------------------
0386:
0387: /**
0388: * Parses a string using the rule set or DecimalFormat belonging
0389: * to this substitution. If there's a match, a mathematical
0390: * operation (the inverse of the one used in formatting) is
0391: * performed on the result of the parse and the value passed in
0392: * and returned as the result. The parse position is updated to
0393: * point to the first unmatched character in the string.
0394: * @param text The string to parse
0395: * @param parsePosition On entry, ignored, but assumed to be 0.
0396: * On exit, this is updated to point to the first unmatched
0397: * character (or 0 if the substitution didn't match)
0398: * @param baseValue A partial parse result that should be
0399: * combined with the result of this parse
0400: * @param upperBound When searching the rule set for a rule
0401: * matching the string passed in, only rules with base values
0402: * lower than this are considered
0403: * @param lenientParse If true and matching against rules fails,
0404: * the substitution will also try matching the text against
0405: * numerals using a default-costructed NumberFormat. If false,
0406: * no extra work is done. (This value is false whenever the
0407: * formatter isn't in lenient-parse mode, but is also false
0408: * under some conditions even when the formatter _is_ in
0409: * lenient-parse mode.)
0410: * @return If there's a match, this is the result of composing
0411: * baseValue with whatever was returned from matching the
0412: * characters. This will be either a Long or a Double. If there's
0413: * no match this is new Long(0) (not null), and parsePosition
0414: * is left unchanged.
0415: */
0416: public Number doParse(String text, ParsePosition parsePosition,
0417: double baseValue, double upperBound, boolean lenientParse) {
0418: Number tempResult;
0419:
0420: // figure out the highest base value a rule can have and match
0421: // the text being parsed (this varies according to the type of
0422: // substitutions: multiplier, modulus, and numerator substitutions
0423: // restrict the search to rules with base values lower than their
0424: // own; same-value substitutions leave the upper bound wherever
0425: // it was, and the others allow any rule to match
0426: upperBound = calcUpperBound(upperBound);
0427:
0428: // use our rule set to parse the text. If that fails and
0429: // lenient parsing is enabled (this is always false if the
0430: // formatter's lenient-parsing mode is off, but it may also
0431: // be false even when the formatter's lenient-parse mode is
0432: // on), then also try parsing the text using a default-
0433: // constructed NumberFormat
0434: if (ruleSet != null) {
0435: tempResult = ruleSet.parse(text, parsePosition, upperBound);
0436: if (lenientParse && !ruleSet.isFractionSet()
0437: && parsePosition.getIndex() == 0) {
0438: tempResult = NumberFormat.getInstance().parse(text,
0439: parsePosition);
0440: }
0441:
0442: // ...or use our DecimalFormat to parse the text
0443: } else {
0444: tempResult = numberFormat.parse(text, parsePosition);
0445: }
0446:
0447: // if the parse was successful, we've already advanced the caller's
0448: // parse position (this is the one function that doesn't have one
0449: // of its own). Derive a parse result and return it as a Long,
0450: // if possible, or a Double
0451: if (parsePosition.getIndex() != 0) {
0452: double result = tempResult.doubleValue();
0453:
0454: // composeRuleValue() produces a full parse result from
0455: // the partial parse result passed to this function from
0456: // the caller (this is either the owning rule's base value
0457: // or the partial result obtained from composing the
0458: // owning rule's base value with its other substitution's
0459: // parse result) and the partial parse result obtained by
0460: // matching the substitution (which will be the same value
0461: // the caller would get by parsing just this part of the
0462: // text with RuleBasedNumberFormat.parse() ). How the two
0463: // values are used to derive the full parse result depends
0464: // on the types of substitutions: For a regular rule, the
0465: // ultimate result is its multiplier substitution's result
0466: // times the rule's divisor (or the rule's base value) plus
0467: // the modulus substitution's result (which will actually
0468: // supersede part of the rule's base value). For a negative-
0469: // number rule, the result is the negative of its substitution's
0470: // result. For a fraction rule, it's the sum of its two
0471: // substitution results. For a rule in a fraction rule set,
0472: // it's the numerator substitution's result divided by
0473: // the rule's base value. Results from same-value substitutions
0474: // propagate back upard, and null substitutions don't affect
0475: // the result.
0476: result = composeRuleValue(result, baseValue);
0477: if (result == (long) result) {
0478: return new Long((long) result);
0479: } else {
0480: return new Double(result);
0481: }
0482:
0483: // if the parse was UNsuccessful, return 0
0484: } else {
0485: return tempResult;
0486: }
0487: }
0488:
0489: /**
0490: * Derives a new value from the two values passed in. The two values
0491: * are typically either the base values of two rules (the one containing
0492: * the substitution and the one matching the substitution) or partial
0493: * parse results derived in some other way. The operation is generally
0494: * the inverse of the operation performed by transformNumber().
0495: * @param newRuleValue The value produced by matching this substitution
0496: * @param oldRuleValue The value that was passed to the substitution
0497: * by the rule that owns it
0498: * @return A third value derived from the other two, representing a
0499: * partial parse result
0500: */
0501: public abstract double composeRuleValue(double newRuleValue,
0502: double oldRuleValue);
0503:
0504: /**
0505: * Calculates an upper bound when searching for a rule that matches
0506: * this substitution. Rules with base values greater than or equal
0507: * to upperBound are not considered.
0508: * @param oldUpperBound The current upper-bound setting. The new
0509: * upper bound can't be any higher.
0510: */
0511: public abstract double calcUpperBound(double oldUpperBound);
0512:
0513: //-----------------------------------------------------------------------
0514: // simple accessors
0515: //-----------------------------------------------------------------------
0516:
0517: /**
0518: * Returns the substitution's position in the rule that owns it.
0519: * @return The substitution's position in the rule that owns it.
0520: */
0521: public final int getPos() {
0522: return pos;
0523: }
0524:
0525: /**
0526: * Returns the character used in the textual representation of
0527: * substitutions of this type. Used by toString().
0528: * @return This substitution's token character.
0529: */
0530: abstract char tokenChar();
0531:
0532: /**
0533: * Returns true if this is a null substitution. (We didn't do this
0534: * with instanceof partially because it causes source files to
0535: * proliferate and partially because we have to port this to C++.)
0536: * @return true if this object is an instance of NullSubstitution
0537: */
0538: public boolean isNullSubstitution() {
0539: return false;
0540: }
0541:
0542: /**
0543: * Returns true if this is a modulus substitution. (We didn't do this
0544: * with instanceof partially because it causes source files to
0545: * proliferate and partially because we have to port this to C++.)
0546: * @return true if this object is an instance of ModulusSubstitution
0547: */
0548: public boolean isModulusSubstitution() {
0549: return false;
0550: }
0551: }
0552:
0553: //===================================================================
0554: // SameValueSubstitution
0555: //===================================================================
0556:
0557: /**
0558: * A substitution that passes the value passed to it through unchanged.
0559: * Represented by == in rule descriptions.
0560: */
0561: class SameValueSubstitution extends NFSubstitution {
0562: //-----------------------------------------------------------------------
0563: // constants
0564: //-----------------------------------------------------------------------
0565:
0566: /**
0567: * Puts a copyright in the .class file
0568: */
0569: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
0570:
0571: //-----------------------------------------------------------------------
0572: // construction
0573: //-----------------------------------------------------------------------
0574:
0575: /**
0576: * Constructs a SameValueSubstution. This function just uses the
0577: * superclass constructor, but it performs a check that this
0578: * substitution doesn't call the rule set that owns it, since that
0579: * would lead to infinite recursion.
0580: */
0581: SameValueSubstitution(int pos, NFRuleSet ruleSet,
0582: RuleBasedNumberFormat formatter, String description) {
0583: super (pos, ruleSet, formatter, description);
0584: if (description.equals("==")) {
0585: throw new IllegalArgumentException(
0586: "== is not a legal token");
0587: }
0588: }
0589:
0590: //-----------------------------------------------------------------------
0591: // formatting
0592: //-----------------------------------------------------------------------
0593:
0594: /**
0595: * Returns "number" unchanged.
0596: * @return "number"
0597: */
0598: public long transformNumber(long number) {
0599: return number;
0600: }
0601:
0602: /**
0603: * Returns "number" unchanged.
0604: * @return "number"
0605: */
0606: public double transformNumber(double number) {
0607: return number;
0608: }
0609:
0610: //-----------------------------------------------------------------------
0611: // parsing
0612: //-----------------------------------------------------------------------
0613:
0614: /**
0615: * Returns newRuleValue and ignores oldRuleValue. (The value we got
0616: * matching the substitution supersedes the value of the rule
0617: * that owns the substitution.)
0618: * @param newRuleValue The value resulting from matching the substituion
0619: * @param oldRuleValue The value of the rule containing the
0620: * substitution.
0621: * @return newRuleValue
0622: */
0623: public double composeRuleValue(double newRuleValue,
0624: double oldRuleValue) {
0625: return newRuleValue;
0626: }
0627:
0628: /**
0629: * SameValueSubstitution doesn't change the upper bound.
0630: * @param oldUpperBound The current upper bound.
0631: * @return oldUpperBound
0632: */
0633: public double calcUpperBound(double oldUpperBound) {
0634: return oldUpperBound;
0635: }
0636:
0637: //-----------------------------------------------------------------------
0638: // simple accessor
0639: //-----------------------------------------------------------------------
0640:
0641: /**
0642: * The token character for a SameValueSubstitution is =.
0643: * @return '='
0644: */
0645: char tokenChar() {
0646: return '=';
0647: }
0648: }
0649:
0650: //===================================================================
0651: // MultiplierSubstitution
0652: //===================================================================
0653:
0654: /**
0655: * A substitution that divides the number being formatted by the rule's
0656: * divisor and formats the quotient. Represented by << in normal
0657: * rules.
0658: */
0659: class MultiplierSubstitution extends NFSubstitution {
0660: //-----------------------------------------------------------------------
0661: // constants
0662: //-----------------------------------------------------------------------
0663:
0664: /**
0665: * Puts a copyright in the .class file
0666: */
0667: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
0668:
0669: //-----------------------------------------------------------------------
0670: // data members
0671: //-----------------------------------------------------------------------
0672:
0673: /**
0674: * The divisor of the rule that owns this substitution.
0675: */
0676: double divisor;
0677:
0678: //-----------------------------------------------------------------------
0679: // construction
0680: //-----------------------------------------------------------------------
0681:
0682: /**
0683: * Constructs a MultiplierSubstitution. This uses the superclass
0684: * constructor to initialize most members, but this substitution
0685: * also maintains its own copy of its rule's divisor.
0686: * @param pos The substitution's position in its rule's rule text
0687: * @param divisor The owning rule's divisor
0688: * @ruleSet The ruleSet this substitution uses to format its result
0689: * @formatter The formatter that owns this substitution
0690: * @description The description describing this substitution
0691: */
0692: MultiplierSubstitution(int pos, double divisor, NFRuleSet ruleSet,
0693: RuleBasedNumberFormat formatter, String description) {
0694: super (pos, ruleSet, formatter, description);
0695:
0696: // the owning rule's divisor affects the behavior of this
0697: // substitution. Rather than keeping a back-pointer to the
0698: // rule, we keep a copy of the divisor
0699: this .divisor = divisor;
0700:
0701: if (divisor == 0) { // this will cause recursion
0702: throw new IllegalStateException(
0703: "Substitution with bad divisor (" + divisor + ") "
0704: + description.substring(0, pos) + " | "
0705: + description.substring(pos));
0706: }
0707: }
0708:
0709: /**
0710: * Sets the substitution's divisor based on the values passed in.
0711: * @param radix The radix of the divisor.
0712: * @param exponent The exponent of the divisor.
0713: */
0714: public void setDivisor(int radix, int exponent) {
0715: divisor = Math.pow(radix, exponent);
0716:
0717: if (divisor == 0) {
0718: throw new IllegalStateException(
0719: "Substitution with divisor 0");
0720: }
0721: }
0722:
0723: //-----------------------------------------------------------------------
0724: // boilerplate
0725: //-----------------------------------------------------------------------
0726:
0727: /**
0728: * Augments the superclass's equals() function by comparing divisors.
0729: * @param that The other substitution
0730: * @return true if the two substitutions are functionally equal
0731: */
0732: public boolean equals(Object that) {
0733: if (super .equals(that)) {
0734: MultiplierSubstitution that2 = (MultiplierSubstitution) that;
0735:
0736: return divisor == that2.divisor;
0737: } else {
0738: return false;
0739: }
0740: }
0741:
0742: //-----------------------------------------------------------------------
0743: // formatting
0744: //-----------------------------------------------------------------------
0745:
0746: /**
0747: * Divides the number by the rule's divisor and returns the quotient.
0748: * @param number The number being formatted.
0749: * @return "number" divided by the rule's divisor
0750: */
0751: public long transformNumber(long number) {
0752: return (long) Math.floor(number / divisor);
0753: }
0754:
0755: /**
0756: * Divides the number by the rule's divisor and returns the quotient.
0757: * This is an integral quotient if we're filling in the substitution
0758: * using another rule set, but it's the full quotient (integral and
0759: * fractional parts) if we're filling in the substitution using
0760: * a DecimalFormat. (This allows things such as "1.2 million".)
0761: * @param number The number being formatted
0762: * @return "number" divided by the rule's divisor
0763: */
0764: public double transformNumber(double number) {
0765: if (ruleSet == null) {
0766: return number / divisor;
0767: } else {
0768: return Math.floor(number / divisor);
0769: }
0770: }
0771:
0772: //-----------------------------------------------------------------------
0773: // parsing
0774: //-----------------------------------------------------------------------
0775:
0776: /**
0777: * Returns newRuleValue times the divisor. Ignores oldRuleValue.
0778: * (The result of matching a << substitution supersedes the base
0779: * value of the rule that contains it.)
0780: * @param newRuleValue The result of matching the substitution
0781: * @param oldRuleValue The base value of the rule containing the
0782: * substitution
0783: * @return newRuleValue * divisor
0784: */
0785: public double composeRuleValue(double newRuleValue,
0786: double oldRuleValue) {
0787: return newRuleValue * divisor;
0788: }
0789:
0790: /**
0791: * Sets the upper bound down to the rule's divisor.
0792: * @param oldUpperBound Ignored.
0793: * @return The rule's divisor.
0794: */
0795: public double calcUpperBound(double oldUpperBound) {
0796: return divisor;
0797: }
0798:
0799: //-----------------------------------------------------------------------
0800: // simple accessor
0801: //-----------------------------------------------------------------------
0802:
0803: /**
0804: * The token character for a multiplier substitution is <.
0805: * @return '<'
0806: */
0807: char tokenChar() {
0808: return '<';
0809: }
0810: }
0811:
0812: //===================================================================
0813: // ModulusSubstitution
0814: //===================================================================
0815:
0816: /**
0817: * A substitution that divides the number being formatted by the its rule's
0818: * divisor and formats the remainder. Represented by ">>" in a
0819: * regular rule.
0820: */
0821: class ModulusSubstitution extends NFSubstitution {
0822: //-----------------------------------------------------------------------
0823: // constants
0824: //-----------------------------------------------------------------------
0825:
0826: /**
0827: * Puts a copyright in the .class file
0828: */
0829: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
0830:
0831: //-----------------------------------------------------------------------
0832: // data members
0833: //-----------------------------------------------------------------------
0834:
0835: /**
0836: * The divisor of the rule owning this substitution
0837: */
0838: double divisor;
0839:
0840: /**
0841: * If this is a >>> substitution, the rule to use to format
0842: * the substitution value. Otherwise, null.
0843: */
0844: NFRule ruleToUse;
0845:
0846: //-----------------------------------------------------------------------
0847: // construction
0848: //-----------------------------------------------------------------------
0849:
0850: /**
0851: * Constructs a ModulusSubstution. In addition to the inherited
0852: * members, a ModulusSubstitution keeps track of the divisor of the
0853: * rule that owns it, and may also keep a reference to the rule
0854: * that precedes the rule containing this substitution in the rule
0855: * set's rule list.
0856: * @param pos The substitution's position in its rule's rule text
0857: * @param divisor The divisor of the rule that owns this substitution
0858: * @param rulePredecessor The rule that precedes this substitution's
0859: * rule in its rule set's rule list
0860: * @param formatter The RuleBasedNumberFormat owning this substitution
0861: * @param description The description for this substitution
0862: */
0863: ModulusSubstitution(int pos, double divisor,
0864: NFRule rulePredecessor, NFRuleSet ruleSet,
0865: RuleBasedNumberFormat formatter, String description) {
0866: super (pos, ruleSet, formatter, description);
0867:
0868: // the owning rule's divisor controls the behavior of this
0869: // substitution: rather than keeping a backpointer to the rule,
0870: // we keep a copy of the divisor
0871: this .divisor = divisor;
0872:
0873: if (divisor == 0) { // this will cause recursion
0874: throw new IllegalStateException(
0875: "Substitution with bad divisor (" + divisor + ") "
0876: + description.substring(0, pos) + " | "
0877: + description.substring(pos));
0878: }
0879:
0880: // the >>> token doesn't alter how this substituion calculates the
0881: // values it uses for formatting and parsing, but it changes
0882: // what's done with that value after it's obtained: >>> short-
0883: // circuits the rule-search process and goes straight to the
0884: // specified rule to format the substitution value
0885: if (description.equals(">>>")) {
0886: ruleToUse = rulePredecessor;
0887: } else {
0888: ruleToUse = null;
0889: }
0890: }
0891:
0892: /**
0893: * Makes the substitution's divisor conform to that of the rule
0894: * that owns it. Used when the divisor is determined after creation.
0895: * @param radix The radix of the divsor.
0896: * @param exponent The exponent of the divisor.
0897: */
0898: public void setDivisor(int radix, int exponent) {
0899: divisor = Math.pow(radix, exponent);
0900:
0901: if (divisor == 0) { // this will cause recursion
0902: throw new IllegalStateException(
0903: "Substitution with bad divisor");
0904: }
0905: }
0906:
0907: //-----------------------------------------------------------------------
0908: // boilerplate
0909: //-----------------------------------------------------------------------
0910:
0911: /**
0912: * Augments the inherited equals() function by comparing divisors and
0913: * ruleToUse.
0914: * @param that The other substitution
0915: * @return true if the two substitutions are functionally equivalent
0916: */
0917: public boolean equals(Object that) {
0918: if (super .equals(that)) {
0919: ModulusSubstitution that2 = (ModulusSubstitution) that;
0920:
0921: return divisor == that2.divisor;
0922: } else {
0923: return false;
0924: }
0925: }
0926:
0927: //-----------------------------------------------------------------------
0928: // formatting
0929: //-----------------------------------------------------------------------
0930:
0931: /**
0932: * If this is a >>> substitution, use ruleToUse to fill in
0933: * the substitution. Otherwise, just use the superclass function.
0934: * @param number The number being formatted
0935: * @toInsertInto The string to insert the result of this substitution
0936: * into
0937: * @param pos The position of the rule text in toInsertInto
0938: */
0939: public void doSubstitution(long number, StringBuffer toInsertInto,
0940: int pos) {
0941: // if this isn't a >>> substitution, just use the inherited version
0942: // of this function (which uses either a rule set or a DecimalFormat
0943: // to format its substitution value)
0944: if (ruleToUse == null) {
0945: super .doSubstitution(number, toInsertInto, pos);
0946:
0947: // a >>> substitution goes straight to a particular rule to
0948: // format the substitution value
0949: } else {
0950: long numberToFormat = transformNumber(number);
0951: ruleToUse.doFormat(numberToFormat, toInsertInto, pos
0952: + this .pos);
0953: }
0954: }
0955:
0956: /**
0957: * If this is a >>> substitution, use ruleToUse to fill in
0958: * the substitution. Otherwise, just use the superclass function.
0959: * @param number The number being formatted
0960: * @toInsertInto The string to insert the result of this substitution
0961: * into
0962: * @param pos The position of the rule text in toInsertInto
0963: */
0964: public void doSubstitution(double number,
0965: StringBuffer toInsertInto, int pos) {
0966: // if this isn't a >>> substitution, just use the inherited version
0967: // of this function (which uses either a rule set or a DecimalFormat
0968: // to format its substitution value)
0969: if (ruleToUse == null) {
0970: super .doSubstitution(number, toInsertInto, pos);
0971:
0972: // a >>> substitution goes straight to a particular rule to
0973: // format the substitution value
0974: } else {
0975: double numberToFormat = transformNumber(number);
0976:
0977: ruleToUse.doFormat(numberToFormat, toInsertInto, pos
0978: + this .pos);
0979: }
0980: }
0981:
0982: /**
0983: * Divides the number being formatted by the rule's divisor and
0984: * returns the remainder.
0985: * @param number The number being formatted
0986: * @return "number" mod divisor
0987: */
0988: public long transformNumber(long number) {
0989: return (long) Math.floor(number % divisor);
0990: }
0991:
0992: /**
0993: * Divides the number being formatted by the rule's divisor and
0994: * returns the remainder.
0995: * @param number The number being formatted
0996: * @return "number" mod divisor
0997: */
0998: public double transformNumber(double number) {
0999: return Math.floor(number % divisor);
1000: }
1001:
1002: //-----------------------------------------------------------------------
1003: // parsing
1004: //-----------------------------------------------------------------------
1005:
1006: /**
1007: * If this is a >>> substitution, match only against ruleToUse.
1008: * Otherwise, use the superclass function.
1009: * @param text The string to parse
1010: * @param parsePosition Ignored on entry, updated on exit to point to
1011: * the first unmatched character.
1012: * @param baseValue The partial parse result prior to calling this
1013: * routine.
1014: */
1015: public Number doParse(String text, ParsePosition parsePosition,
1016: double baseValue, double upperBound, boolean lenientParse) {
1017: // if this isn't a >>> substitution, we can just use the
1018: // inherited parse() routine to do the parsing
1019: if (ruleToUse == null) {
1020: return super .doParse(text, parsePosition, baseValue,
1021: upperBound, lenientParse);
1022:
1023: // but if it IS a >>> substitution, we have to do it here: we
1024: // use the specific rule's doParse() method, and then we have to
1025: // do some of the other work of NFRuleSet.parse()
1026: } else {
1027: Number tempResult = ruleToUse.doParse(text, parsePosition,
1028: false, upperBound);
1029:
1030: if (parsePosition.getIndex() != 0) {
1031: double result = tempResult.doubleValue();
1032:
1033: result = composeRuleValue(result, baseValue);
1034: if (result == (long) result) {
1035: return new Long((long) result);
1036: } else {
1037: return new Double(result);
1038: }
1039: } else {
1040: return tempResult;
1041: }
1042: }
1043: }
1044:
1045: /**
1046: * Returns the highest multiple of the rule's divisor that its less
1047: * than or equal to oldRuleValue, plus newRuleValue. (The result
1048: * is the sum of the result of parsing the substitution plus the
1049: * base valueof the rule containing the substitution, but if the
1050: * owning rule's base value isn't an even multiple of its divisor,
1051: * we have to round it down to a multiple of the divisor, or we
1052: * get unwanted digits in the result.)
1053: * @param newRuleValue The result of parsing the substitution
1054: * @param oldRuleValue The base value of the rule containing the
1055: * substitution
1056: * @return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue
1057: */
1058: public double composeRuleValue(double newRuleValue,
1059: double oldRuleValue) {
1060: return (oldRuleValue - (oldRuleValue % divisor)) + newRuleValue;
1061: }
1062:
1063: /**
1064: * Sets the upper bound down to the owning rule's divisor
1065: * @param oldUpperBound Ignored
1066: * @return The owning rule's dvisor
1067: */
1068: public double calcUpperBound(double oldUpperBound) {
1069: return divisor;
1070: }
1071:
1072: //-----------------------------------------------------------------------
1073: // simple accessors
1074: //-----------------------------------------------------------------------
1075:
1076: /**
1077: * Returns true. This _is_ a ModulusSubstitution.
1078: * @return true
1079: */
1080: public boolean isModulusSubstitution() {
1081: return true;
1082: }
1083:
1084: /**
1085: * The token character of a ModulusSubstitution is >.
1086: * @return '>'
1087: */
1088: char tokenChar() {
1089: return '>';
1090: }
1091: }
1092:
1093: //===================================================================
1094: // IntegralPartSubstitution
1095: //===================================================================
1096:
1097: /**
1098: * A substitution that formats the number's integral part. This is
1099: * represented by << in a fraction rule.
1100: */
1101: class IntegralPartSubstitution extends NFSubstitution {
1102: //-----------------------------------------------------------------------
1103: // constants
1104: //-----------------------------------------------------------------------
1105:
1106: /**
1107: * Puts a copyright in the .class file
1108: */
1109: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1110:
1111: //-----------------------------------------------------------------------
1112: // construction
1113: //-----------------------------------------------------------------------
1114:
1115: /**
1116: * Constructs an IntegralPartSubstitution. This just calls
1117: * the superclass constructor.
1118: */
1119: IntegralPartSubstitution(int pos, NFRuleSet ruleSet,
1120: RuleBasedNumberFormat formatter, String description) {
1121: super (pos, ruleSet, formatter, description);
1122: }
1123:
1124: //-----------------------------------------------------------------------
1125: // formatting
1126: //-----------------------------------------------------------------------
1127:
1128: /**
1129: * Returns the number's integral part. (For a long, that's just the
1130: * number unchanged.)
1131: * @param number The number being formatted
1132: * @return "number" unchanged
1133: */
1134: public long transformNumber(long number) {
1135: return number;
1136: }
1137:
1138: /**
1139: * Returns the number's integral part.
1140: * @param number The integral part of the number being formatted
1141: * @return floor(number)
1142: */
1143: public double transformNumber(double number) {
1144: return Math.floor(number);
1145: }
1146:
1147: //-----------------------------------------------------------------------
1148: // parsing
1149: //-----------------------------------------------------------------------
1150:
1151: /**
1152: * Returns the sum of the result of parsing the substitution and the
1153: * owning rule's base value. (The owning rule, at best, has an
1154: * integral-part substitution and a fractional-part substitution,
1155: * so we can safely just add them.)
1156: * @param newRuleValue The result of matching the substitution
1157: * @param oldRuleValue The partial result of the parse prior to
1158: * calling this function
1159: * @return oldRuleValue + newRuleValue
1160: */
1161: public double composeRuleValue(double newRuleValue,
1162: double oldRuleValue) {
1163: return newRuleValue + oldRuleValue;
1164: }
1165:
1166: /**
1167: * An IntegralPartSubstitution sets the upper bound back up so all
1168: * potentially matching rules are considered.
1169: * @param oldUpperBound Ignored
1170: * @return Double.MAX_VALUE
1171: */
1172: public double calcUpperBound(double oldUpperBound) {
1173: return Double.MAX_VALUE;
1174: }
1175:
1176: //-----------------------------------------------------------------------
1177: // simple accessor
1178: //-----------------------------------------------------------------------
1179:
1180: /**
1181: * An IntegralPartSubstitution's token character is <
1182: * @return '<'
1183: */
1184: char tokenChar() {
1185: return '<';
1186: }
1187: }
1188:
1189: //===================================================================
1190: // FractionalPartSubstitution
1191: //===================================================================
1192:
1193: /**
1194: * A substitution that formats the fractional part of a number. This is
1195: * represented by >> in a fraction rule.
1196: */
1197: class FractionalPartSubstitution extends NFSubstitution {
1198: //-----------------------------------------------------------------------
1199: // constants
1200: //-----------------------------------------------------------------------
1201:
1202: /**
1203: * Puts a copyright in the .class file
1204: */
1205: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1206:
1207: //-----------------------------------------------------------------------
1208: // data members
1209: //-----------------------------------------------------------------------
1210:
1211: /**
1212: * true if this substitution should have the default "by digits"
1213: * behavior, false otherwise
1214: */
1215: private boolean byDigits = false;
1216:
1217: /**
1218: * true if we automatically insert spaces to separate names of digits
1219: * set to false by '>>>' in fraction rules, used by Thai.
1220: */
1221: private boolean useSpaces = true;
1222:
1223: /**
1224: * The largest number of digits after the decimal point that this
1225: * object will show in "by digits" mode
1226: */
1227: private static final int MAXDECIMALDIGITS = 18; // 8
1228:
1229: //-----------------------------------------------------------------------
1230: // construction
1231: //-----------------------------------------------------------------------
1232:
1233: /**
1234: * Constructs a FractionalPartSubstitution. This object keeps a flag
1235: * telling whether it should format by digits or not. In addition,
1236: * it marks the rule set it calls (if any) as a fraction rule set.
1237: */
1238: FractionalPartSubstitution(int pos, NFRuleSet ruleSet,
1239: RuleBasedNumberFormat formatter, String description) {
1240: super (pos, ruleSet, formatter, description);
1241: // boolean chevron = description.startsWith(">>") || ruleSet == this.ruleSet;
1242: // if (chevron || ruleSet == this.ruleSet) {
1243:
1244: if (description.equals(">>") || description.equals(">>>")
1245: || ruleSet == this .ruleSet) {
1246: byDigits = true;
1247: if (description.equals(">>>")) {
1248: useSpaces = false;
1249: }
1250: } else {
1251: this .ruleSet.makeIntoFractionRuleSet();
1252: }
1253: }
1254:
1255: //-----------------------------------------------------------------------
1256: // formatting
1257: //-----------------------------------------------------------------------
1258:
1259: /**
1260: * If in "by digits" mode, fills in the substitution one decimal digit
1261: * at a time using the rule set containing this substitution.
1262: * Otherwise, uses the superclass function.
1263: * @param number The number being formatted
1264: * @param toInsertInto The string to insert the result of formatting
1265: * the substitution into
1266: * @param pos The position of the owning rule's rule text in
1267: * toInsertInto
1268: */
1269: public void doSubstitution(double number,
1270: StringBuffer toInsertInto, int pos) {
1271: // if we're not in "byDigits" mode, just use the inherited
1272: // doSubstitution() routine
1273: if (!byDigits) {
1274: super .doSubstitution(number, toInsertInto, pos);
1275:
1276: // if we're in "byDigits" mode, transform the value into an integer
1277: // by moving the decimal point eight places to the right and
1278: // pulling digits off the right one at a time, formatting each digit
1279: // as an integer using this substitution's owning rule set
1280: // (this is slower, but more accurate, than doing it from the
1281: // other end)
1282: } else {
1283: // int numberToFormat = (int)Math.round(transformNumber(number) * Math.pow(
1284: // 10, MAXDECIMALDIGITS));
1285: // long numberToFormat = (long)Math.round(transformNumber(number) * Math.pow(10, MAXDECIMALDIGITS));
1286:
1287: // just print to string and then use that
1288: DigitList dl = new DigitList();
1289: dl.set(number, 20, true);
1290:
1291: // this flag keeps us from formatting trailing zeros. It starts
1292: // out false because we're pulling from the right, and switches
1293: // to true the first time we encounter a non-zero digit
1294: // boolean doZeros = false;
1295: // System.out.println("class: " + getClass().getName());
1296: // System.out.println("number: " + number + " transformed: " + transformNumber(number));
1297: // System.out.println("formatting " + numberToFormat);
1298: // for (int i = 0; i < MAXDECIMALDIGITS; i++) {
1299: // int digit = (int)(numberToFormat % 10);
1300: // System.out.println(" #: '" + numberToFormat + "'" + " digit '" + digit + "'");
1301: // if (digit != 0 || doZeros) {
1302: // if (doZeros && useSpaces) {
1303: // toInsertInto.insert(pos + this.pos, ' ');
1304: // }
1305: // doZeros = true;
1306: // ruleSet.format(digit, toInsertInto, pos + this.pos);
1307: // }
1308: // numberToFormat /= 10;
1309: // }
1310:
1311: boolean pad = false;
1312: while (dl.count > Math.max(0, dl.decimalAt)) {
1313: if (pad && useSpaces) {
1314: toInsertInto.insert(pos + this .pos, ' ');
1315: } else {
1316: pad = true;
1317: }
1318: ruleSet.format(dl.digits[--dl.count] - '0',
1319: toInsertInto, pos + this .pos);
1320: }
1321: while (dl.decimalAt < 0) {
1322: if (pad && useSpaces) {
1323: toInsertInto.insert(pos + this .pos, ' ');
1324: } else {
1325: pad = true;
1326: }
1327: ruleSet.format(0, toInsertInto, pos + this .pos);
1328: ++dl.decimalAt;
1329: }
1330: }
1331: }
1332:
1333: /**
1334: * Returns the fractional part of the number, which will always be
1335: * zero if it's a long.
1336: * @param number The number being formatted
1337: * @return 0
1338: */
1339: public long transformNumber(long number) {
1340: return 0;
1341: }
1342:
1343: /**
1344: * Returns the fractional part of the number.
1345: * @param number The number being formatted.
1346: * @return number - floor(number)
1347: */
1348: public double transformNumber(double number) {
1349: return number - Math.floor(number);
1350: }
1351:
1352: //-----------------------------------------------------------------------
1353: // parsing
1354: //-----------------------------------------------------------------------
1355:
1356: /**
1357: * If in "by digits" mode, parses the string as if it were a string
1358: * of individual digits; otherwise, uses the superclass function.
1359: * @param text The string to parse
1360: * @param parsePosition Ignored on entry, but updated on exit to point
1361: * to the first unmatched character
1362: * @param baseValue The partial parse result prior to entering this
1363: * function
1364: * @param upperBound Only consider rules with base values lower than
1365: * this when filling in the substitution
1366: * @param lenientParse If true, try matching the text as numerals if
1367: * matching as words doesn't work
1368: * @return If the match was successful, the current partial parse
1369: * result; otherwise new Long(0). The result is either a Long or
1370: * a Double.
1371: */
1372: public Number doParse(String text, ParsePosition parsePosition,
1373: double baseValue, double upperBound, boolean lenientParse) {
1374: // if we're not in byDigits mode, we can just use the inherited
1375: // doParse()
1376: if (!byDigits) {
1377: return super .doParse(text, parsePosition, baseValue, 0,
1378: lenientParse);
1379:
1380: // if we ARE in byDigits mode, parse the text one digit at a time
1381: // using this substitution's owning rule set (we do this by setting
1382: // upperBound to 10 when calling doParse() ) until we reach
1383: // nonmatching text
1384: } else {
1385: String workText = new String(text);
1386: ParsePosition workPos = new ParsePosition(1);
1387: double result = 0;
1388: int digit;
1389: // double p10 = 0.1;
1390:
1391: // while (workText.length() > 0 && workPos.getIndex() != 0) {
1392: // workPos.setIndex(0);
1393: // digit = ruleSet.parse(workText, workPos, 10).intValue();
1394: // if (lenientParse && workPos.getIndex() == 0) {
1395: // digit = NumberFormat.getInstance().parse(workText, workPos).intValue();
1396: // }
1397:
1398: // if (workPos.getIndex() != 0) {
1399: // result += digit * p10;
1400: // p10 /= 10;
1401: // parsePosition.setIndex(parsePosition.getIndex() + workPos.getIndex());
1402: // workText = workText.substring(workPos.getIndex());
1403: // while (workText.length() > 0 && workText.charAt(0) == ' ') {
1404: // workText = workText.substring(1);
1405: // parsePosition.setIndex(parsePosition.getIndex() + 1);
1406: // }
1407: // }
1408: // }
1409:
1410: DigitList dl = new DigitList();
1411: while (workText.length() > 0 && workPos.getIndex() != 0) {
1412: workPos.setIndex(0);
1413: digit = ruleSet.parse(workText, workPos, 10).intValue();
1414: if (lenientParse && workPos.getIndex() == 0) {
1415: digit = NumberFormat.getInstance().parse(workText,
1416: workPos).intValue();
1417: }
1418:
1419: if (workPos.getIndex() != 0) {
1420: dl.append('0' + digit);
1421:
1422: parsePosition.setIndex(parsePosition.getIndex()
1423: + workPos.getIndex());
1424: workText = workText.substring(workPos.getIndex());
1425: while (workText.length() > 0
1426: && workText.charAt(0) == ' ') {
1427: workText = workText.substring(1);
1428: parsePosition
1429: .setIndex(parsePosition.getIndex() + 1);
1430: }
1431: }
1432: }
1433: result = dl.count == 0 ? 0 : dl.getDouble();
1434:
1435: result = composeRuleValue(result, baseValue);
1436: return new Double(result);
1437: }
1438: }
1439:
1440: /**
1441: * Returns the sum of the two partial parse results.
1442: * @param newRuleValue The result of parsing the substitution
1443: * @param oldRuleValue The partial parse result prior to calling
1444: * this function
1445: * @return newRuleValue + oldRuleValue
1446: */
1447: public double composeRuleValue(double newRuleValue,
1448: double oldRuleValue) {
1449: return newRuleValue + oldRuleValue;
1450: }
1451:
1452: /**
1453: * Not used.
1454: */
1455: public double calcUpperBound(double oldUpperBound) {
1456: return 0; // this value is ignored
1457: }
1458:
1459: //-----------------------------------------------------------------------
1460: // simple accessor
1461: //-----------------------------------------------------------------------
1462:
1463: /**
1464: * The token character for a FractionalPartSubstitution is >.
1465: * @return '>'
1466: */
1467: char tokenChar() {
1468: return '>';
1469: }
1470: }
1471:
1472: //===================================================================
1473: // AbsoluteValueSubstitution
1474: //===================================================================
1475:
1476: /**
1477: * A substitution that formats the absolute value of the number.
1478: * This substition is represented by >> in a negative-number rule.
1479: */
1480: class AbsoluteValueSubstitution extends NFSubstitution {
1481: //-----------------------------------------------------------------------
1482: // constants
1483: //-----------------------------------------------------------------------
1484:
1485: /**
1486: * Puts a copyright in the .class file
1487: */
1488: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1489:
1490: //-----------------------------------------------------------------------
1491: // construction
1492: //-----------------------------------------------------------------------
1493:
1494: /**
1495: * Constructs an AbsoluteValueSubstitution. This just uses the
1496: * superclass constructor.
1497: */
1498: AbsoluteValueSubstitution(int pos, NFRuleSet ruleSet,
1499: RuleBasedNumberFormat formatter, String description) {
1500: super (pos, ruleSet, formatter, description);
1501: }
1502:
1503: //-----------------------------------------------------------------------
1504: // formatting
1505: //-----------------------------------------------------------------------
1506:
1507: /**
1508: * Returns the absolute value of the number.
1509: * @param number The number being formatted.
1510: * @return abs(number)
1511: */
1512: public long transformNumber(long number) {
1513: return Math.abs(number);
1514: }
1515:
1516: /**
1517: * Returns the absolute value of the number.
1518: * @param number The number being formatted.
1519: * @return abs(number)
1520: */
1521: public double transformNumber(double number) {
1522: return Math.abs(number);
1523: }
1524:
1525: //-----------------------------------------------------------------------
1526: // parsing
1527: //-----------------------------------------------------------------------
1528:
1529: /**
1530: * Returns the addtive inverse of the result of parsing the
1531: * substitution (this supersedes the earlier partial result)
1532: * @param newRuleValue The result of parsing the substitution
1533: * @param oldRuleValue The partial parse result prior to calling
1534: * this function
1535: * @return -newRuleValue
1536: */
1537: public double composeRuleValue(double newRuleValue,
1538: double oldRuleValue) {
1539: return -newRuleValue;
1540: }
1541:
1542: /**
1543: * Sets the upper bound beck up to consider all rules
1544: * @param oldUpperBound Ignored.
1545: * @return Double.MAX_VALUE
1546: */
1547: public double calcUpperBound(double oldUpperBound) {
1548: return Double.MAX_VALUE;
1549: }
1550:
1551: //-----------------------------------------------------------------------
1552: // simple accessor
1553: //-----------------------------------------------------------------------
1554:
1555: /**
1556: * The token character for an AbsoluteValueSubstitution is >
1557: * @return '>'
1558: */
1559: char tokenChar() {
1560: return '>';
1561: }
1562: }
1563:
1564: //===================================================================
1565: // NumeratorSubstitution
1566: //===================================================================
1567:
1568: /**
1569: * A substitution that multiplies the number being formatted (which is
1570: * between 0 and 1) by the base value of the rule that owns it and
1571: * formats the result. It is represented by << in the rules
1572: * in a fraction rule set.
1573: */
1574: class NumeratorSubstitution extends NFSubstitution {
1575: //-----------------------------------------------------------------------
1576: // constants
1577: //-----------------------------------------------------------------------
1578:
1579: /**
1580: * Puts a copyright in the .class file
1581: */
1582: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1583:
1584: //-----------------------------------------------------------------------
1585: // data members
1586: //-----------------------------------------------------------------------
1587:
1588: /**
1589: * The denominator of the fraction we're finding the numerator for.
1590: * (The base value of the rule that owns this substitution.)
1591: */
1592: double denominator;
1593:
1594: /**
1595: * True if we format leading zeros (this is a hack for Hebrew spellout)
1596: */
1597: boolean withZeros;
1598:
1599: //-----------------------------------------------------------------------
1600: // construction
1601: //-----------------------------------------------------------------------
1602:
1603: /**
1604: * Constructs a NumberatorSubstitution. In addition to the inherited
1605: * fields, a NumeratorSubstitution keeps track of a denominator, which
1606: * is merely the base value of the rule that owns it.
1607: */
1608: NumeratorSubstitution(int pos, double denominator,
1609: NFRuleSet ruleSet, RuleBasedNumberFormat formatter,
1610: String description) {
1611: super (pos, ruleSet, formatter, fixdesc(description));
1612:
1613: // this substitution's behavior depends on the rule's base value
1614: // Rather than keeping a backpointer to the rule, we copy its
1615: // base value here
1616: this .denominator = denominator;
1617:
1618: this .withZeros = description.endsWith("<<");
1619: }
1620:
1621: static String fixdesc(String description) {
1622: return description.endsWith("<<") ? description.substring(0,
1623: description.length() - 1) : description;
1624: }
1625:
1626: //-----------------------------------------------------------------------
1627: // boilerplate
1628: //-----------------------------------------------------------------------
1629:
1630: /**
1631: * Tests two NumeratorSubstitutions for equality
1632: * @param that The other NumeratorSubstitution
1633: * @return true if the two objects are functionally equivalent
1634: */
1635: public boolean equals(Object that) {
1636: if (super .equals(that)) {
1637: NumeratorSubstitution that2 = (NumeratorSubstitution) that;
1638: return denominator == that2.denominator;
1639: } else {
1640: return false;
1641: }
1642: }
1643:
1644: //-----------------------------------------------------------------------
1645: // formatting
1646: //-----------------------------------------------------------------------
1647:
1648: /**
1649: * Performs a mathematical operation on the number, formats it using
1650: * either ruleSet or decimalFormat, and inserts the result into
1651: * toInsertInto.
1652: * @param number The number being formatted.
1653: * @param toInsertInto The string we insert the result into
1654: * @param pos The position in toInsertInto where the owning rule's
1655: * rule text begins (this value is added to this substitution's
1656: * position to determine exactly where to insert the new text)
1657: */
1658: public void doSubstitution(double number,
1659: StringBuffer toInsertInto, int pos) {
1660: // perform a transformation on the number being formatted that
1661: // is dependent on the type of substitution this is
1662: String s = toInsertInto.toString();
1663: double numberToFormat = transformNumber(number);
1664:
1665: if (withZeros && ruleSet != null) {
1666: // if there are leading zeros in the decimal expansion then emit them
1667: long nf = (long) numberToFormat;
1668: int len = toInsertInto.length();
1669: while ((nf *= 10) < denominator) {
1670: toInsertInto.insert(pos + this .pos, ' ');
1671: ruleSet.format(0, toInsertInto, pos + this .pos);
1672: }
1673: pos += toInsertInto.length() - len;
1674: }
1675:
1676: // if the result is an integer, from here on out we work in integer
1677: // space (saving time and memory and preserving accuracy)
1678: if (numberToFormat == Math.floor(numberToFormat)
1679: && ruleSet != null) {
1680: ruleSet.format((long) numberToFormat, toInsertInto, pos
1681: + this .pos);
1682:
1683: // if the result isn't an integer, then call either our rule set's
1684: // format() method or our DecimalFormat's format() method to
1685: // format the result
1686: } else {
1687: if (ruleSet != null) {
1688: ruleSet.format(numberToFormat, toInsertInto, pos
1689: + this .pos);
1690: } else {
1691: toInsertInto.insert(pos + this .pos, numberFormat
1692: .format(numberToFormat));
1693: }
1694: }
1695: }
1696:
1697: /**
1698: * Returns the number being formatted times the denominator.
1699: * @param number The number being formatted
1700: * @return number * denominator
1701: */
1702: public long transformNumber(long number) {
1703: return Math.round(number * denominator);
1704: }
1705:
1706: /**
1707: * Returns the number being formatted times the denominator.
1708: * @param number The number being formatted
1709: * @return number * denominator
1710: */
1711: public double transformNumber(double number) {
1712: return Math.round(number * denominator);
1713: }
1714:
1715: //-----------------------------------------------------------------------
1716: // parsing
1717: //-----------------------------------------------------------------------
1718:
1719: /**
1720: * Dispatches to the inherited version of this function, but makes
1721: * sure that lenientParse is off.
1722: */
1723: public Number doParse(String text, ParsePosition parsePosition,
1724: double baseValue, double upperBound, boolean lenientParse) {
1725: // we don't have to do anything special to do the parsing here,
1726: // but we have to turn lenient parsing off-- if we leave it on,
1727: // it SERIOUSLY messes up the algorithm
1728:
1729: // if withZeros is true, we need to count the zeros
1730: // and use that to adjust the parse result
1731: int zeroCount = 0;
1732: if (withZeros) {
1733: String workText = new String(text);
1734: ParsePosition workPos = new ParsePosition(1);
1735: int digit;
1736:
1737: while (workText.length() > 0 && workPos.getIndex() != 0) {
1738: workPos.setIndex(0);
1739: digit = ruleSet.parse(workText, workPos, 1).intValue(); // parse zero or nothing at all
1740: if (workPos.getIndex() == 0) {
1741: // we failed, either there were no more zeros, or the number was formatted with digits
1742: // either way, we're done
1743: break;
1744: }
1745:
1746: ++zeroCount;
1747: parsePosition.setIndex(parsePosition.getIndex()
1748: + workPos.getIndex());
1749: workText = workText.substring(workPos.getIndex());
1750: while (workText.length() > 0
1751: && workText.charAt(0) == ' ') {
1752: workText = workText.substring(1);
1753: parsePosition
1754: .setIndex(parsePosition.getIndex() + 1);
1755: }
1756: }
1757:
1758: text = text.substring(parsePosition.getIndex()); // arrgh!
1759: parsePosition.setIndex(0);
1760: }
1761:
1762: // we've parsed off the zeros, now let's parse the rest from our current position
1763: Number result = super .doParse(text, parsePosition,
1764: withZeros ? 1 : baseValue, upperBound, false);
1765:
1766: if (withZeros) {
1767: // any base value will do in this case. is there a way to
1768: // force this to not bother trying all the base values?
1769:
1770: // compute the 'effective' base and prescale the value down
1771: long n = result.longValue();
1772: long d = 1;
1773: int pow = 0;
1774: while (d <= n) {
1775: d *= 10;
1776: ++pow;
1777: }
1778: // now add the zeros
1779: while (zeroCount > 0) {
1780: d *= 10;
1781: --zeroCount;
1782: }
1783: // d is now our true denominator
1784: result = new Double(n / (double) d);
1785: }
1786:
1787: return result;
1788: }
1789:
1790: /**
1791: * Divides the result of parsing the substitution by the partial
1792: * parse result.
1793: * @param newRuleValue The result of parsing the substitution
1794: * @param oldRuleValue The owning rule's base value
1795: * @return newRuleValue / oldRuleValue
1796: */
1797: public double composeRuleValue(double newRuleValue,
1798: double oldRuleValue) {
1799: return newRuleValue / oldRuleValue;
1800: }
1801:
1802: /**
1803: * Sets the uper bound down to this rule's base value
1804: * @param oldUpperBound Ignored
1805: * @return The base value of the rule owning this substitution
1806: */
1807: public double calcUpperBound(double oldUpperBound) {
1808: return denominator;
1809: }
1810:
1811: //-----------------------------------------------------------------------
1812: // simple accessor
1813: //-----------------------------------------------------------------------
1814:
1815: /**
1816: * The token character for a NumeratorSubstitution is <
1817: * @return '<'
1818: */
1819: char tokenChar() {
1820: return '<';
1821: }
1822: }
1823:
1824: //===================================================================
1825: // NullSubstitution
1826: //===================================================================
1827:
1828: /**
1829: * A substitution which does nothing. This class exists just to simplify
1830: * the logic in some other routines so that they don't have to worry
1831: * about how many substitutions a rule has.
1832: */
1833: class NullSubstitution extends NFSubstitution {
1834: //-----------------------------------------------------------------------
1835: // constants
1836: //-----------------------------------------------------------------------
1837:
1838: /**
1839: * Puts a copyright in the .class file
1840: */
1841: private static final String copyrightNotice = "Copyright \u00a91997-1998 IBM Corp. All rights reserved.";
1842:
1843: //-----------------------------------------------------------------------
1844: // construction
1845: //-----------------------------------------------------------------------
1846:
1847: /**
1848: * Constructs a NullSubstitution. This just delegates to the superclass
1849: * constructor, but the only value we really care about is the position.
1850: */
1851: NullSubstitution(int pos, NFRuleSet ruleSet,
1852: RuleBasedNumberFormat formatter, String description) {
1853: super (pos, ruleSet, formatter, description);
1854: }
1855:
1856: //-----------------------------------------------------------------------
1857: // boilerplate
1858: //-----------------------------------------------------------------------
1859:
1860: /**
1861: * Only checks for class equality
1862: */
1863: public boolean equals(Object that) {
1864: return super .equals(that);
1865: }
1866:
1867: /**
1868: * NullSubstitutions don't show up in the textual representation
1869: * of a RuleBasedNumberFormat
1870: */
1871: public String toString() {
1872: return "";
1873: }
1874:
1875: //-----------------------------------------------------------------------
1876: // formatting
1877: //-----------------------------------------------------------------------
1878:
1879: /**
1880: * Does nothing.
1881: */
1882: public void doSubstitution(long number, StringBuffer toInsertInto,
1883: int pos) {
1884: }
1885:
1886: /**
1887: * Does nothing.
1888: */
1889: public void doSubstitution(double number,
1890: StringBuffer toInsertInto, int pos) {
1891: }
1892:
1893: /**
1894: * Never called.
1895: */
1896: ///CLOVER:OFF
1897: public long transformNumber(long number) {
1898: return 0;
1899: }
1900:
1901: ///CLOVER:ON
1902:
1903: /**
1904: * Never called.
1905: */
1906: ///CLOVER:OFF
1907: public double transformNumber(double number) {
1908: return 0;
1909: }
1910:
1911: ///CLOVER:ON
1912:
1913: //-----------------------------------------------------------------------
1914: // parsing
1915: //-----------------------------------------------------------------------
1916:
1917: /**
1918: * Returns the partial parse result unchanged
1919: */
1920: public Number doParse(String text, ParsePosition parsePosition,
1921: double baseValue, double upperBound, boolean lenientParse) {
1922: if (baseValue == (long) baseValue) {
1923: return new Long((long) baseValue);
1924: } else {
1925: return new Double(baseValue);
1926: }
1927: }
1928:
1929: /**
1930: * Never called.
1931: */
1932: ///CLOVER:OFF
1933: public double composeRuleValue(double newRuleValue,
1934: double oldRuleValue) {
1935: return 0;
1936: }
1937:
1938: ///CLOVER:ON
1939:
1940: /**
1941: * Never called.
1942: */
1943: ///CLOVER:OFF
1944: public double calcUpperBound(double oldUpperBound) {
1945: return 0;
1946: }
1947:
1948: ///CLOVER:ON
1949:
1950: //-----------------------------------------------------------------------
1951: // simple accessors
1952: //-----------------------------------------------------------------------
1953:
1954: /**
1955: * Returns true (this _is_ a NullSubstitution).
1956: * @return true
1957: */
1958: public boolean isNullSubstitution() {
1959: return true;
1960: }
1961:
1962: /**
1963: * Never called.
1964: */
1965: ///CLOVER:OFF
1966: char tokenChar() {
1967: return ' ';
1968: }
1969: ///CLOVER:ON
1970: }
|