Source Code Cross Referenced for FormatNumber2.java in  » XML » saxonb » net » sf » saxon » functions » 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 » XML » saxonb » net.sf.saxon.functions 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        package net.sf.saxon.functions;
002:
003:        import net.sf.saxon.Controller;
004:        import net.sf.saxon.expr.Expression;
005:        import net.sf.saxon.expr.StaticContext;
006:        import net.sf.saxon.expr.Token;
007:        import net.sf.saxon.expr.XPathContext;
008:        import net.sf.saxon.om.*;
009:        import net.sf.saxon.style.ExpressionContext;
010:        import net.sf.saxon.tinytree.CharSlice;
011:        import net.sf.saxon.trans.*;
012:        import net.sf.saxon.value.*;
013:
014:        import java.io.Serializable;
015:        import java.math.BigDecimal;
016:        import java.util.ArrayList;
017:        import java.util.List;
018:
019:        /**
020:         * XSLT 2.0 implementation of format-number() function - removes the dependence on the JDK.
021:         */
022:
023:        public class FormatNumber2 extends SystemFunction implements 
024:                XSLTFunction {
025:
026:            private NamespaceResolver nsContext = null;
027:            // held only if the third argument is present, and its value is not known statically
028:
029:            private DecimalSymbols decimalFormatSymbols = null;
030:            // held only if the decimal format to use can be determined statically
031:
032:            private transient String picture = null;
033:            // held transiently at compile time if the picture is known statically
034:
035:            private SubPicture[] subPictures = null;
036:            // held if the picture is known statically
037:
038:            private boolean requireFixup = false;
039:            // used to detect when an unknown decimal-format name is used
040:
041:            private transient boolean checked = false;
042:
043:            // the second time checkArguments is called, it's a global check so the static context is inaccurate
044:
045:            public void checkArguments(StaticContext env) throws XPathException {
046:                if (checked)
047:                    return;
048:                checked = true;
049:                super .checkArguments(env);
050:                if (argument[1] instanceof  StringValue) {
051:                    // picture is known statically - optimize for this common case
052:                    picture = ((StringValue) argument[1]).getStringValue();
053:                }
054:                if (argument.length == 3) {
055:                    if (argument[2] instanceof  StringValue) {
056:                        // common case, decimal format name is supplied as a string literal
057:
058:                        String qname = ((StringValue) argument[2])
059:                                .getStringValue();
060:                        String dfLocalName;
061:                        String dfURI;
062:                        try {
063:                            String[] parts = env.getConfiguration()
064:                                    .getNameChecker().getQNameParts(qname);
065:                            dfLocalName = parts[1];
066:                            dfURI = env.getURIForPrefix(parts[0]);
067:                        } catch (QNameException e) {
068:                            throw new StaticError(
069:                                    "Invalid decimal format name. "
070:                                            + e.getMessage());
071:                        }
072:
073:                        DecimalFormatManager dfm = ((ExpressionContext) env)
074:                                .getXSLStylesheet().getDecimalFormatManager();
075:                        requireFixup = true;
076:                        dfm.registerUsage(dfURI, dfLocalName, this );
077:                        // this causes a callback to the fixup() method, either now, or later if it's a forwards reference
078:                    } else {
079:                        // we need to save the namespace context
080:                        nsContext = env.getNamespaceResolver();
081:                    }
082:                } else {
083:                    // two arguments only: it uses the default decimal format
084:                    if (env instanceof  ExpressionContext) {
085:                        // this is XSLT
086:                        DecimalFormatManager dfm = ((ExpressionContext) env)
087:                                .getXSLStylesheet().getDecimalFormatManager();
088:                        dfm.registerUsage("", "", this );
089:                        // Note: if using the "default default", there will be no fixup call.
090:                    } else {
091:                        // using saxon:decimal-format in some other environment
092:                    }
093:                }
094:            }
095:
096:            /**
097:             * Fixup: this is a callback from the DecimalFormatManager used once the xsl:decimal-format
098:             * element is identified
099:             */
100:
101:            public void fixup(DecimalSymbols dfs) {
102:                // System.err.println("Fixed up format-number, picture=" + picture);
103:                requireFixup = false;
104:                decimalFormatSymbols = dfs;
105:                if (picture != null) {
106:                    try {
107:                        subPictures = getSubPictures(picture, dfs);
108:                    } catch (XPathException err) {
109:                        subPictures = null;
110:                        // we'll report the error at run-time
111:                    }
112:                }
113:            }
114:
115:            /**
116:             * Analyze a picture string into two sub-pictures.
117:             * @return an array of two sub-pictures, the positive and the negative sub-pictures respectively.
118:             * If there is only one sub-picture, the second one is null.
119:             */
120:
121:            private SubPicture[] getSubPictures(String picture,
122:                    DecimalSymbols dfs) throws XPathException {
123:                int[] picture4 = StringValue.expand(picture);
124:                SubPicture[] pics = new SubPicture[2];
125:                if (picture4.length == 0) {
126:                    DynamicError err = new DynamicError(
127:                            "format-number() picture is zero-length");
128:                    err.setErrorCode("XTDE1310");
129:                    throw err;
130:                }
131:                int sep = -1;
132:                for (int c = 0; c < picture4.length; c++) {
133:                    if (picture4[c] == dfs.patternSeparator) {
134:                        if (c == 0) {
135:                            grumble("first subpicture is zero-length");
136:                        } else if (sep >= 0) {
137:                            grumble("more than one pattern separator");
138:                        } else if (sep == picture4.length - 1) {
139:                            grumble("second subpicture is zero-length");
140:                        }
141:                        sep = c;
142:                    }
143:                }
144:
145:                if (sep < 0) {
146:                    pics[0] = new SubPicture(picture4, dfs);
147:                    pics[1] = null;
148:                } else {
149:                    int[] pic0 = new int[sep];
150:                    System.arraycopy(picture4, 0, pic0, 0, sep);
151:                    int[] pic1 = new int[picture4.length - sep - 1];
152:                    System.arraycopy(picture4, sep + 1, pic1, 0,
153:                            picture4.length - sep - 1);
154:                    pics[0] = new SubPicture(pic0, dfs);
155:                    pics[1] = new SubPicture(pic1, dfs);
156:                }
157:                return pics;
158:            }
159:
160:            /**
161:             * preEvaluate: this method suppresses compile-time evaluation by doing nothing.
162:             * We can't evaluate early because we don't have access to the DecimalFormatManager.
163:             */
164:
165:            public Expression preEvaluate(StaticContext env)
166:                    throws XPathException {
167:                return this ;
168:            }
169:
170:            /**
171:             * Evaluate in a context where a string is wanted
172:             */
173:
174:            public String evaluateAsString(XPathContext context)
175:                    throws XPathException {
176:
177:                int numArgs = argument.length;
178:                Controller ctrl = context.getController();
179:
180:                DecimalSymbols dfs = decimalFormatSymbols;
181:
182:                AtomicValue av0 = (AtomicValue) argument[0]
183:                        .evaluateItem(context);
184:                if (av0 == null) {
185:                    av0 = DoubleValue.NaN;
186:                }
187:                ;
188:                NumericValue number = (NumericValue) av0.getPrimitiveValue();
189:
190:                if (dfs == null) {
191:                    // the decimal-format name was not resolved statically
192:                    if (requireFixup) {
193:                        // we registered for a fixup, but none came
194:                        dynamicError("Unknown decimal format name", "XTDE1280",
195:                                context);
196:                        return null;
197:                    }
198:                    DecimalFormatManager dfm = ctrl.getExecutable()
199:                            .getDecimalFormatManager();
200:                    if (numArgs == 2) {
201:                        dfs = dfm.getDefaultDecimalFormat();
202:                    } else {
203:                        // the decimal-format name was given as a run-time expression
204:                        String qname = argument[2].evaluateItem(context)
205:                                .getStringValue();
206:                        try {
207:                            String[] parts = ctrl.getConfiguration()
208:                                    .getNameChecker().getQNameParts(qname);
209:                            String localName = parts[1];
210:                            String uri = nsContext.getURIForPrefix(parts[0],
211:                                    false);
212:                            if (uri == null) {
213:                                dynamicError("Namespace prefix '" + parts[0]
214:                                        + "' has not been defined", "XTDE1280",
215:                                        context);
216:                                return null;
217:                            }
218:                            dfs = dfm.getNamedDecimalFormat(uri, localName);
219:                            if (dfs == null) {
220:                                dynamicError(
221:                                        "format-number function: decimal-format '"
222:                                                + localName
223:                                                + "' is not defined",
224:                                        "XTDE1280", context);
225:                                return null;
226:                            }
227:                        } catch (QNameException e) {
228:                            dynamicError("Invalid decimal format name. "
229:                                    + e.getMessage(), "XTDE1280", context);
230:                        }
231:                    }
232:                }
233:                SubPicture[] pics = subPictures;
234:                if (pics == null) {
235:                    String format = argument[1].evaluateItem(context)
236:                            .getStringValue();
237:                    pics = getSubPictures(format, dfs);
238:                }
239:                return formatNumber(number, pics, dfs).toString();
240:            }
241:
242:            /**
243:             * Evaluate in a general context
244:             */
245:
246:            public Item evaluateItem(XPathContext c) throws XPathException {
247:                return new StringValue(evaluateAsString(c));
248:            }
249:
250:            /**
251:             * Format a number, given the two subpictures and the decimal format symbols
252:             */
253:
254:            private CharSequence formatNumber(NumericValue number,
255:                    SubPicture[] subPictures, DecimalSymbols dfs) {
256:
257:                NumericValue absN = number;
258:                SubPicture pic;
259:                String minusSign = "";
260:                if (number.signum() < 0) {
261:                    absN = number.negate();
262:                    if (subPictures[1] == null) {
263:                        pic = subPictures[0];
264:                        minusSign = "" + unicodeChar(dfs.minusSign);
265:                    } else {
266:                        pic = subPictures[1];
267:                    }
268:                } else {
269:                    pic = subPictures[0];
270:                }
271:
272:                return pic.format(absN, dfs, minusSign);
273:            }
274:
275:            private void grumble(String s) throws XPathException {
276:                dynamicError("format-number picture: " + s, "XTDE1310", null);
277:            }
278:
279:            /**
280:             * Convert a double to a BigDecimal. In general there will be several BigDecimal values that
281:             * are equal to the supplied value, and the one we want to choose is the one with fewest non-zero
282:             * digits. The algorithm used is rather pragmatic: look for a string of zeroes or nines, try rounding
283:             * the number down or up as approriate, then convert the adjusted value to a double to see if it's
284:             * equal to the original: if not, use the original value unchanged.
285:             * @param value the double to be converted
286:             * @param precision 2 for a double, 1 for a float
287:             * @return the result of conversion to a double
288:             */
289:
290:            public static BigDecimal adjustToDecimal(double value, int precision) {
291:                final String zeros = (precision == 1 ? "00000" : "000000000");
292:                final String nines = (precision == 1 ? "99999" : "999999999");
293:                BigDecimal initial = new BigDecimal(value);
294:                BigDecimal trial = null;
295:                String s = DecimalValue.decimalToString(initial);
296:                int start = (s.charAt(0) == '-' ? 1 : 0);
297:                int p = s.indexOf(".");
298:                int i = s.lastIndexOf(zeros);
299:                if (i > 0) {
300:                    if (p < 0 || i < p) {
301:                        // we're in the integer part
302:                        // try replacing all following digits with zeros and seeing if we get the same double back
303:                        FastStringBuffer sb = new FastStringBuffer(s.length());
304:                        sb.append(s.substring(0, i));
305:                        for (int n = i; n < s.length(); n++) {
306:                            sb.append(s.charAt(n) == '.' ? '.' : '0');
307:                        }
308:                        trial = new BigDecimal(sb.toString());
309:                    } else {
310:                        // we're in the fractional part
311:                        // try truncating the number before the zeros and seeing if we get the same double back
312:                        trial = new BigDecimal(s.substring(0, i));
313:
314:                    }
315:                } else {
316:                    i = s.indexOf(nines);
317:                    if (i >= 0) {
318:                        if (i == start) {
319:                            // number starts with 99999... or -99999. Try rounding up to 100000.. or -100000...
320:                            FastStringBuffer sb = new FastStringBuffer(s
321:                                    .length() + 1);
322:                            if (start == 1) {
323:                                sb.append('-');
324:                            }
325:                            sb.append('1');
326:                            for (int n = start; n < s.length(); n++) {
327:                                sb.append(s.charAt(n) == '.' ? '.' : '0');
328:                            }
329:                            trial = new BigDecimal(sb.toString());
330:                        } else {
331:                            // try rounding up
332:                            while (i >= 0
333:                                    && (s.charAt(i) == '9' || s.charAt(i) == '.')) {
334:                                i--;
335:                            }
336:                            if (i < 0 || s.charAt(i) == '-') {
337:                                return initial; // can't happen: we've already handled numbers starting 99999..
338:                            } else if (p < 0 || i < p) {
339:                                // we're in the integer part
340:                                FastStringBuffer sb = new FastStringBuffer(s
341:                                        .length());
342:                                sb.append(s.substring(0, i));
343:                                sb.append((char) ((int) s.charAt(i) + 1));
344:                                for (int n = i; n < s.length(); n++) {
345:                                    sb.append(s.charAt(n) == '.' ? '.' : '0');
346:                                }
347:                                trial = new BigDecimal(sb.toString());
348:                            } else {
349:                                // we're in the fractional part - can ignore following digits
350:                                String s2 = s.substring(0, i)
351:                                        + (char) ((int) s.charAt(i) + 1);
352:                                trial = new BigDecimal(s2);
353:                            }
354:                        }
355:                    }
356:                }
357:                if (trial != null
358:                        && (precision == 1 ? trial.floatValue() == value
359:                                : trial.doubleValue() == value)) {
360:                    return trial;
361:                } else {
362:                    return initial;
363:                }
364:            }
365:
366:            /**
367:             * Inner class to represent one sub-picture (the negative or positive subpicture)
368:             */
369:
370:            private class SubPicture implements  Serializable {
371:
372:                int minWholePartSize = 0;
373:                int maxWholePartSize = 0;
374:                int minFractionPartSize = 0;
375:                int maxFractionPartSize = 0;
376:                boolean isPercent = false;
377:                boolean isPerMille = false;
378:                String prefix = "";
379:                String suffix = "";
380:                int[] wholePartGroupingPositions = null;
381:                int[] fractionalPartGroupingPositions = null;
382:
383:                public SubPicture(int[] pic, DecimalSymbols dfs)
384:                        throws XPathException {
385:
386:                    final int percentSign = dfs.percent;
387:                    final int perMilleSign = dfs.permill;
388:                    final int decimalSeparator = dfs.decimalSeparator;
389:                    final int groupingSeparator = dfs.groupingSeparator;
390:                    final int digitSign = dfs.digit;
391:                    final int zeroDigit = dfs.zeroDigit;
392:
393:                    List wholePartPositions = null;
394:                    List fractionalPartPositions = null;
395:
396:                    boolean foundDigit = false;
397:                    boolean foundDecimalSeparator = false;
398:                    for (int i = 0; i < pic.length; i++) {
399:                        if (pic[i] == digitSign || pic[i] == zeroDigit) {
400:                            foundDigit = true;
401:                            break;
402:                        }
403:                    }
404:                    if (!foundDigit) {
405:                        grumble("subpicture contains no digit or zero-digit sign");
406:                    }
407:
408:                    int phase = 0;
409:                    // phase = 0: passive characters at start
410:                    // phase = 1: digit signs in whole part
411:                    // phase = 2: zero-digit signs in whole part
412:                    // phase = 3: zero-digit signs in fractional part
413:                    // phase = 4: digit signs in fractional part
414:                    // phase = 5: passive characters at end
415:
416:                    for (int i = 0; i < pic.length; i++) {
417:                        int c = pic[i];
418:
419:                        if (c == percentSign || c == perMilleSign) {
420:                            if (isPercent || isPerMille) {
421:                                grumble("Cannot have more than one percent or per-mille character in a sub-picture");
422:                            }
423:                            isPercent = (c == percentSign);
424:                            isPerMille = (c == perMilleSign);
425:                            switch (phase) {
426:                            case 0:
427:                                prefix += unicodeChar(c);
428:                                break;
429:                            case 1:
430:                            case 2:
431:                            case 3:
432:                            case 4:
433:                            case 5:
434:                                phase = 5;
435:                                suffix += unicodeChar(c);
436:                                break;
437:                            }
438:                        } else if (c == digitSign) {
439:                            switch (phase) {
440:                            case 0:
441:                            case 1:
442:                                phase = 1;
443:                                maxWholePartSize++;
444:                                break;
445:                            case 2:
446:                                grumble("Digit sign must not appear after a zero-digit sign in the integer part of a sub-picture");
447:                                break;
448:                            case 3:
449:                            case 4:
450:                                phase = 4;
451:                                maxFractionPartSize++;
452:                                break;
453:                            case 5:
454:                                grumble("Passive character must not appear between active characters in a sub-picture");
455:                                break;
456:                            }
457:                        } else if (c == zeroDigit) {
458:                            switch (phase) {
459:                            case 0:
460:                            case 1:
461:                            case 2:
462:                                phase = 2;
463:                                minWholePartSize++;
464:                                maxWholePartSize++;
465:                                break;
466:                            case 3:
467:                                minFractionPartSize++;
468:                                maxFractionPartSize++;
469:                                break;
470:                            case 4:
471:                                grumble("Zero digit sign must not appear after a digit sign in the fractional part of a sub-picture");
472:                                break;
473:                            case 5:
474:                                grumble("Passive character must not appear between active characters in a sub-picture");
475:                                break;
476:                            }
477:                        } else if (c == decimalSeparator) {
478:                            switch (phase) {
479:                            case 0:
480:                            case 1:
481:                            case 2:
482:                                phase = 3;
483:                                foundDecimalSeparator = true;
484:                                break;
485:                            case 3:
486:                            case 4:
487:                            case 5:
488:                                if (foundDecimalSeparator) {
489:                                    grumble("There must only be one decimal separator in a sub-picture");
490:                                } else {
491:                                    grumble("Decimal separator cannot come after a character in the suffix");
492:                                }
493:                                break;
494:                            }
495:                        } else if (c == groupingSeparator) {
496:                            switch (phase) {
497:                            case 0:
498:                            case 1:
499:                            case 2:
500:                                if (wholePartPositions == null) {
501:                                    wholePartPositions = new ArrayList(3);
502:                                }
503:                                wholePartPositions.add(new Integer(
504:                                        maxWholePartSize));
505:                                // note these are positions from a false offset, they will be corrected later
506:                                break;
507:                            case 3:
508:                            case 4:
509:                                if (maxFractionPartSize == 0) {
510:                                    grumble("Grouping separator cannot be adjacent to decimal separator");
511:                                }
512:                                if (fractionalPartPositions == null) {
513:                                    fractionalPartPositions = new ArrayList(3);
514:                                }
515:                                fractionalPartPositions.add(new Integer(
516:                                        maxFractionPartSize));
517:                                break;
518:                            case 5:
519:                                grumble("Grouping separator found in suffix of sub-picture");
520:                                break;
521:                            }
522:                        } else { // passive character found
523:                            switch (phase) {
524:                            case 0:
525:                                prefix += unicodeChar(c);
526:                                break;
527:                            case 1:
528:                            case 2:
529:                            case 3:
530:                            case 4:
531:                            case 5:
532:                                phase = 5;
533:                                suffix += unicodeChar(c);
534:                                break;
535:                            }
536:                        }
537:                    }
538:
539:                    // System.err.println("minWholePartSize = " + minWholePartSize);
540:                    // System.err.println("maxWholePartSize = " + maxWholePartSize);
541:                    // System.err.println("minFractionPartSize = " + minFractionPartSize);
542:                    // System.err.println("maxFractionPartSize = " + maxFractionPartSize);
543:
544:                    // Sort out the grouping positions
545:
546:                    if (wholePartPositions != null) {
547:                        // convert to positions relative to the decimal separator
548:                        int n = wholePartPositions.size();
549:                        wholePartGroupingPositions = new int[n];
550:                        for (int i = 0; i < n; i++) {
551:                            wholePartGroupingPositions[i] = maxWholePartSize
552:                                    - ((Integer) wholePartPositions.get(n - i
553:                                            - 1)).intValue();
554:                        }
555:                        if (n > 1) {
556:                            boolean regular = true;
557:                            int first = wholePartGroupingPositions[0];
558:                            for (int i = 1; i < n; i++) {
559:                                if (wholePartGroupingPositions[i] != i * first) {
560:                                    regular = false;
561:                                    break;
562:                                }
563:                            }
564:                            if (regular) {
565:                                wholePartGroupingPositions = new int[1];
566:                                wholePartGroupingPositions[0] = first;
567:                            }
568:                        }
569:                        if (wholePartGroupingPositions[0] == 0) {
570:                            grumble("Cannot have a grouping separator adjacent to the decimal separator");
571:                        }
572:                    }
573:
574:                    if (fractionalPartPositions != null) {
575:                        int n = fractionalPartPositions.size();
576:                        fractionalPartGroupingPositions = new int[n];
577:                        for (int i = 0; i < n; i++) {
578:                            fractionalPartGroupingPositions[i] = ((Integer) fractionalPartPositions
579:                                    .get(i)).intValue();
580:                        }
581:                    }
582:                }
583:
584:                /**
585:                 * Format a number using this sub-picture
586:                 * @param value the absolute value of the number to be formatted
587:                 */
588:
589:                public CharSequence format(NumericValue value,
590:                        DecimalSymbols dfs, String minusSign) {
591:
592:                    // System.err.println("Formatting " + value);
593:
594:                    if (value.isNaN()) {
595:                        return prefix + dfs.NaN + suffix;
596:                    }
597:
598:                    if (value instanceof  DoubleValue
599:                            && Double.isInfinite(value.getDoubleValue())) {
600:                        return minusSign + prefix + dfs.infinity + suffix;
601:                    }
602:
603:                    if (value instanceof  FloatValue
604:                            && Double.isInfinite(value.getDoubleValue())) {
605:                        return minusSign + prefix + dfs.infinity + suffix;
606:                    }
607:
608:                    int multiplier = 1;
609:                    if (isPercent) {
610:                        multiplier = 100;
611:                    } else if (isPerMille) {
612:                        multiplier = 1000;
613:                    }
614:
615:                    if (multiplier != 1) {
616:                        try {
617:                            value = value.arithmetic(Token.MULT,
618:                                    new IntegerValue(multiplier), null);
619:                        } catch (XPathException e) {
620:                            value = new DoubleValue(value.getDoubleValue()
621:                                    * multiplier);
622:                        }
623:                    }
624:
625:                    StringBuffer sb = new StringBuffer(20);
626:                    if (value instanceof  DoubleValue
627:                            || value instanceof  FloatValue) {
628:                        BigDecimal dec = adjustToDecimal(
629:                                value.getDoubleValue(), 2);
630:                        formatDecimal(dec, sb);
631:
632:                        //formatDouble(value.getDoubleValue(), sb);
633:
634:                    } else if (value instanceof  IntegerValue
635:                            || value instanceof  BigIntegerValue) {
636:                        formatInteger(value, sb);
637:
638:                    } else if (value instanceof  DecimalValue) {
639:                        formatDecimal(((DecimalValue) value).getValue(), sb);
640:                    }
641:
642:                    // System.err.println("Justified number: " + sb.toString());
643:
644:                    // Map the digits and decimal point to use the selected characters
645:
646:                    int[] ib = StringValue.expand(sb);
647:                    int ibused = ib.length;
648:                    int point = sb.indexOf(".");
649:                    if (point == -1) {
650:                        point = sb.length();
651:                    } else {
652:                        ib[point] = dfs.decimalSeparator;
653:
654:                        // If there is no fractional part, delete the decimal point
655:                        if (maxFractionPartSize == 0) {
656:                            ibused--;
657:                        }
658:                    }
659:
660:                    // Map the digits
661:
662:                    if (dfs.zeroDigit != '0') {
663:                        int newZero = dfs.zeroDigit;
664:                        for (int i = 0; i < ibused; i++) {
665:                            int c = ib[i];
666:                            if (c >= '0' && c <= '9') {
667:                                ib[i] = (c - '0' + newZero);
668:                            }
669:                        }
670:                    }
671:
672:                    // Add the whole-part grouping separators
673:
674:                    if (wholePartGroupingPositions != null) {
675:                        if (wholePartGroupingPositions.length == 1) {
676:                            // grouping separators are at regular positions
677:                            int g = wholePartGroupingPositions[0];
678:                            int p = point - g;
679:                            while (p > 0) {
680:                                ib = insert(ib, ibused++,
681:                                        dfs.groupingSeparator, p);
682:                                //sb.insert(p, unicodeChar(dfs.groupingSeparator));
683:                                p -= g;
684:                            }
685:                        } else {
686:                            // grouping separators are at irregular positions
687:                            for (int i = 0; i < wholePartGroupingPositions.length; i++) {
688:                                int p = point - wholePartGroupingPositions[i];
689:                                if (p > 0) {
690:                                    ib = insert(ib, ibused++,
691:                                            dfs.groupingSeparator, p);
692:                                    //sb.insert(p, unicodeChar(dfs.groupingSeparator));
693:                                }
694:                            }
695:                        }
696:                    }
697:
698:                    // Add the fractional-part grouping separators
699:
700:                    if (fractionalPartGroupingPositions != null) {
701:                        // grouping separators are at irregular positions.
702:                        for (int i = 0; i < fractionalPartGroupingPositions.length; i++) {
703:                            int p = point + 1
704:                                    + fractionalPartGroupingPositions[i] + i;
705:                            if (p < ibused - 1) {
706:                                ib = insert(ib, ibused++,
707:                                        dfs.groupingSeparator, p);
708:                                //sb.insert(p, dfs.groupingSeparator);
709:                            } else {
710:                                break;
711:                            }
712:                        }
713:                    }
714:
715:                    // System.err.println("Grouped number: " + sb.toString());
716:
717:                    //sb.insert(0, prefix);
718:                    //sb.insert(0, minusSign);
719:                    //sb.append(suffix);
720:                    FastStringBuffer res = new FastStringBuffer(prefix.length()
721:                            + minusSign.length() + suffix.length() + ibused);
722:                    res.append(minusSign);
723:                    res.append(prefix);
724:                    res.append(StringValue.contract(ib, ibused));
725:                    res.append(suffix);
726:                    return res;
727:                }
728:
729:                /**
730:                 * Format a number supplied as a decimal
731:                 * @param dval the decimal value
732:                 * @param sb the stringBuffer to contain the result
733:                 */
734:                private void formatDecimal(BigDecimal dval, StringBuffer sb) {
735:                    dval = dval.setScale(maxFractionPartSize,
736:                            BigDecimal.ROUND_HALF_EVEN);
737:                    sb.append(dval.toString());
738:
739:                    int point = sb.indexOf(".");
740:                    int intDigits;
741:                    if (point >= 0) {
742:                        int zz = maxFractionPartSize - minFractionPartSize;
743:                        while (zz > 0) {
744:                            if (sb.charAt(sb.length() - 1) == '0') {
745:                                sb.setLength(sb.length() - 1);
746:                                zz--;
747:                            } else {
748:                                break;
749:                            }
750:                        }
751:                        intDigits = point;
752:                        if (sb.charAt(sb.length() - 1) == '.') {
753:                            sb.setLength(sb.length() - 1);
754:                        }
755:                    } else {
756:                        intDigits = sb.length();
757:                    }
758:                    for (int i = 0; i < (minWholePartSize - intDigits); i++) {
759:                        sb.insert(0, '0');
760:                    }
761:                }
762:
763:                /**
764:                 * Format a number supplied as a integer
765:                 * @param value the integer value
766:                 * @param sb the stringBuffer to contain the result
767:                 */
768:
769:                private void formatInteger(NumericValue value, StringBuffer sb) {
770:                    sb.append(value.toString());
771:                    int leadingZeroes = minWholePartSize - sb.length();
772:                    for (int i = 0; i < leadingZeroes; i++) {
773:                        sb.insert(0, '0');
774:                    }
775:                    if (minFractionPartSize != 0) {
776:                        sb.append('.');
777:                        for (int i = 0; i < minFractionPartSize; i++) {
778:                            sb.append('0');
779:                        }
780:                    }
781:                }
782:            }
783:
784:            /**
785:             * Convert a Unicode character (possibly >65536) to a String, using a surrogate pair if necessary
786:             * @param ch the Unicode codepoint value
787:             * @return a string representing the Unicode codepoint, either a string of one character or a surrogate pair
788:             */
789:
790:            private static CharSequence unicodeChar(int ch) {
791:                if (ch < 65536) {
792:                    return "" + (char) ch;
793:                } else { // output a surrogate pair
794:                    //To compute the numeric value of the character corresponding to a surrogate
795:                    //pair, use this formula (all numbers are hex):
796:                    //(FirstChar - D800) * 400 + (SecondChar - DC00) + 10000
797:                    ch -= 65536;
798:                    char[] sb = new char[2];
799:                    sb[0] = ((char) ((ch / 1024) + 55296));
800:                    sb[1] = ((char) ((ch % 1024) + 56320));
801:                    return new CharSlice(sb, 0, 2);
802:                }
803:            }
804:
805:            /**
806:             * Insert an integer into an array of integers
807:             */
808:
809:            private static int[] insert(int[] array, int used, int value,
810:                    int position) {
811:                if (used + 1 > array.length) {
812:                    int[] a2 = new int[used + 10];
813:                    System.arraycopy(array, 0, a2, 0, used);
814:                    array = a2;
815:                }
816:                for (int i = used - 1; i >= position; i--) {
817:                    array[i + 1] = array[i];
818:                }
819:                array[position] = value;
820:                return array;
821:            }
822:        }
823:
824:        //
825:        // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
826:        // you may not use this file except in compliance with the License. You may obtain a copy of the
827:        // License at http://www.mozilla.org/MPL/
828:        //
829:        // Software distributed under the License is distributed on an "AS IS" basis,
830:        // WITHOUT WARRANTY OF ANY KIND, either express or implied.
831:        // See the License for the specific language governing rights and limitations under the License.
832:        //
833:        // The Original Code is: all this file.
834:        //
835:        // The Initial Developer of the Original Code is Michael H. Kay
836:        //
837:        // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
838:        //
839:        // Contributor(s): none.
840:        //
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.