Source Code Cross Referenced for Sprintf.java in  » Scripting » jruby » org » jruby » util » 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 » Scripting » jruby » org.jruby.util 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /***** BEGIN LICENSE BLOCK *****
0002:         * Version: CPL 1.0/GPL 2.0/LGPL 2.1
0003:         *
0004:         * The contents of this file are subject to the Common Public
0005:         * License Version 1.0 (the "License"); you may not use this file
0006:         * except in compliance with the License. You may obtain a copy of
0007:         * the License at http://www.eclipse.org/legal/cpl-v10.html
0008:         *
0009:         * Software distributed under the License is distributed on an "AS
0010:         * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
0011:         * implied. See the License for the specific language governing
0012:         * rights and limitations under the License.
0013:         *
0014:         * Copyright (C) 2007 William N Dortch <bill.dortch@gmail.com>
0015:         *
0016:         * Alternatively, the contents of this file may be used under the terms of
0017:         * either of the GNU General Public License Version 2 or later (the "GPL"),
0018:         * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
0019:         * in which case the provisions of the GPL or the LGPL are applicable instead
0020:         * of those above. If you wish to allow use of your version of this file only
0021:         * under the terms of either the GPL or the LGPL, and not to allow others to
0022:         * use your version of this file under the terms of the CPL, indicate your
0023:         * decision by deleting the provisions above and replace them with the notice
0024:         * and other provisions required by the GPL or the LGPL. If you do not delete
0025:         * the provisions above, a recipient may use your version of this file under
0026:         * the terms of any one of the CPL, the GPL or the LGPL.
0027:         ***** END LICENSE BLOCK *****/package org.jruby.util;
0028:
0029:        import java.math.BigInteger;
0030:        import java.text.DecimalFormatSymbols;
0031:        import java.util.List;
0032:        import java.util.Locale;
0033:
0034:        import org.jruby.Ruby;
0035:        import org.jruby.RubyArray;
0036:        import org.jruby.RubyBignum;
0037:        import org.jruby.RubyFixnum;
0038:        import org.jruby.RubyFloat;
0039:        import org.jruby.RubyInteger;
0040:        import org.jruby.RubyKernel;
0041:        import org.jruby.RubyNumeric;
0042:        import org.jruby.RubyString;
0043:        import org.jruby.runtime.ClassIndex;
0044:        import org.jruby.runtime.MethodIndex;
0045:        import org.jruby.runtime.builtin.IRubyObject;
0046:
0047:        /**
0048:         * @author Bill Dortch
0049:         *
0050:         */
0051:        public class Sprintf {
0052:            private static final int FLAG_NONE = 0;
0053:            private static final int FLAG_SPACE = 1 << 0;
0054:            private static final int FLAG_ZERO = 1 << 1;
0055:            private static final int FLAG_PLUS = 1 << 2;
0056:            private static final int FLAG_MINUS = 1 << 3;
0057:            private static final int FLAG_SHARP = 1 << 4;
0058:            private static final int FLAG_WIDTH = 1 << 5;
0059:            private static final int FLAG_PRECISION = 1 << 6;
0060:
0061:            private static final byte[] PREFIX_OCTAL = { '0' };
0062:            private static final byte[] PREFIX_HEX_LC = { '0', 'x' };
0063:            private static final byte[] PREFIX_HEX_UC = { '0', 'X' };
0064:            private static final byte[] PREFIX_BINARY_LC = { '0', 'b' };
0065:            private static final byte[] PREFIX_BINARY_UC = { '0', 'B' };
0066:
0067:            private static final byte[] PREFIX_NEGATIVE = { '.', '.' };
0068:
0069:            private static final byte[] NAN_VALUE = { 'N', 'a', 'N' };
0070:            private static final byte[] INFINITY_VALUE = { 'I', 'n', 'f' };
0071:
0072:            private static final BigInteger BIG_32 = BigInteger
0073:                    .valueOf(((long) Integer.MAX_VALUE + 1L) << 1);
0074:            private static final BigInteger BIG_64 = BIG_32.shiftLeft(32);
0075:            private static final BigInteger BIG_MINUS_32 = BigInteger
0076:                    .valueOf((long) Integer.MIN_VALUE << 1);
0077:            private static final BigInteger BIG_MINUS_64 = BIG_MINUS_32
0078:                    .shiftLeft(32);
0079:
0080:            private static final int INITIAL_BUFFER_SIZE = 32;
0081:
0082:            private static final String ERR_MALFORMED_FORMAT = "malformed format string";
0083:            private static final String ERR_MALFORMED_NUM = "malformed format string - %[0-9]";
0084:            private static final String ERR_MALFORMED_DOT_NUM = "malformed format string - %.[0-9]";
0085:            private static final String ERR_MALFORMED_STAR_NUM = "malformed format string - %*[0-9]";
0086:            private static final String ERR_ILLEGAL_FORMAT_CHAR = "illegal format character - %";
0087:
0088:            private static class Args {
0089:                Ruby runtime;
0090:                Locale locale;
0091:                IRubyObject rubyObject;
0092:                List rubyArray;
0093:                int length;
0094:                int unnumbered; // last index (+1) accessed by next()
0095:                int numbered; // last index (+1) accessed by get()
0096:
0097:                Args(Locale locale, IRubyObject rubyObject) {
0098:                    if (rubyObject == null) {
0099:                        throw new IllegalArgumentException(
0100:                                "null IRubyObject passed to sprintf");
0101:                    }
0102:                    this .locale = locale == null ? Locale.getDefault() : locale;
0103:                    this .rubyObject = rubyObject;
0104:                    if (rubyObject instanceof  RubyArray) {
0105:                        this .rubyArray = ((RubyArray) rubyObject).getList();
0106:                        this .length = rubyArray.size();
0107:                    } else {
0108:                        this .length = 1;
0109:                    }
0110:                    this .runtime = rubyObject.getRuntime();
0111:                }
0112:
0113:                Args(IRubyObject rubyObject) {
0114:                    this (Locale.getDefault(), rubyObject);
0115:                }
0116:
0117:                // temporary hack to handle non-Ruby values
0118:                // will come up with better solution shortly
0119:                Args(Ruby runtime, long value) {
0120:                    this (RubyFixnum.newFixnum(runtime, value));
0121:                }
0122:
0123:                final void raiseArgumentError(String message) {
0124:                    throw runtime.newArgumentError(message);
0125:                }
0126:
0127:                final void warn(String message) {
0128:                    runtime.getWarnings().warn(message);
0129:                }
0130:
0131:                final void warning(String message) {
0132:                    runtime.getWarnings().warning(message);
0133:                }
0134:
0135:                final IRubyObject next() {
0136:                    // this is the order in which MRI does these two tests
0137:                    if (numbered > 0) {
0138:                        raiseArgumentError("unnumbered" + (unnumbered + 1)
0139:                                + "mixed with numbered");
0140:                    }
0141:                    if (unnumbered >= length) {
0142:                        raiseArgumentError("too few arguments");
0143:                    }
0144:
0145:                    IRubyObject object = rubyArray == null ? rubyObject
0146:                            : (IRubyObject) rubyArray.get(unnumbered);
0147:                    unnumbered++;
0148:                    return object;
0149:                }
0150:
0151:                final IRubyObject get(int index) {
0152:                    // this is the order in which MRI does these tests
0153:                    if (unnumbered > 0) {
0154:                        raiseArgumentError("numbered(" + numbered
0155:                                + ") after unnumbered(" + unnumbered + ")");
0156:                    }
0157:                    if (index < 0) {
0158:                        raiseArgumentError("invalid index - " + (index + 1)
0159:                                + '$');
0160:                    }
0161:                    if (index >= length) {
0162:                        raiseArgumentError("too few arguments");
0163:                    }
0164:
0165:                    numbered = index + 1;
0166:                    return (rubyArray == null ? rubyObject
0167:                            : (IRubyObject) rubyArray.get(index));
0168:                }
0169:
0170:                final IRubyObject getNth(int formatIndex) {
0171:                    return get(formatIndex - 1);
0172:                }
0173:
0174:                final int nextInt() {
0175:                    return intValue(next());
0176:                }
0177:
0178:                final int getInt(int index) {
0179:                    return intValue(get(index));
0180:                }
0181:
0182:                final int getNthInt(int formatIndex) {
0183:                    return intValue(get(formatIndex - 1));
0184:                }
0185:
0186:                final int intValue(IRubyObject obj) {
0187:                    if (obj instanceof  RubyNumeric) {
0188:                        return (int) ((RubyNumeric) obj).getLongValue();
0189:                    }
0190:
0191:                    // basically just forcing a TypeError here to match MRI
0192:                    obj = obj.convertToType(obj.getRuntime().getFixnum(),
0193:                            MethodIndex.TO_INT, "to_int", true);
0194:                    return (int) ((RubyFixnum) obj).getLongValue();
0195:                }
0196:
0197:                final byte getDecimalSeparator() {
0198:                    // not saving DFS instance, as it will only be used once (at most) per call
0199:                    return (byte) new DecimalFormatSymbols(locale)
0200:                            .getDecimalSeparator();
0201:                }
0202:            } // Args
0203:
0204:            /*
0205:             * Using this class to buffer output during formatting, rather than
0206:             * the eventual ByteList itself. That way this buffer can be initialized
0207:             * to a size large enough to prevent reallocations (in most cases), while
0208:             * the resultant ByteList will only be as large as necessary.
0209:             * 
0210:             * (Also, the Buffer class's buffer grows by a factor of 2, as opposed
0211:             * to ByteList's 1.5, which I felt might result in a lot of reallocs.)
0212:             */
0213:            private static class Buffer {
0214:                byte[] buf;
0215:                int size;
0216:
0217:                Buffer() {
0218:                    buf = new byte[INITIAL_BUFFER_SIZE];
0219:                }
0220:
0221:                Buffer(int initialSize) {
0222:                    buf = new byte[initialSize];
0223:                }
0224:
0225:                final void write(int b) {
0226:                    int newSize = size + 1;
0227:                    if (newSize > buf.length) {
0228:                        byte[] newBuf = new byte[Math.max(buf.length << 1,
0229:                                newSize)];
0230:                        System.arraycopy(buf, 0, newBuf, 0, size);
0231:                        buf = newBuf;
0232:                    }
0233:                    buf[size] = (byte) (b & 0xff);
0234:                    size = newSize;
0235:                }
0236:
0237:                final void write(byte[] b, int off, int len) {
0238:                    if (len <= 0 || off < 0)
0239:                        return;
0240:
0241:                    int newSize = size + len;
0242:                    if (newSize > buf.length) {
0243:                        byte[] newBuf = new byte[Math.max(buf.length << 1,
0244:                                newSize)];
0245:                        System.arraycopy(buf, 0, newBuf, 0, size);
0246:                        buf = newBuf;
0247:                    }
0248:                    System.arraycopy(b, off, buf, size, len);
0249:                    size = newSize;
0250:                }
0251:
0252:                final void write(byte[] b) {
0253:                    write(b, 0, b.length);
0254:                }
0255:
0256:                final void fill(int b, int len) {
0257:                    if (len <= 0)
0258:                        return;
0259:
0260:                    int newSize = size + len;
0261:                    if (newSize > buf.length) {
0262:                        byte[] newBuf = new byte[Math.max(buf.length << 1,
0263:                                newSize)];
0264:                        System.arraycopy(buf, 0, newBuf, 0, size);
0265:                        buf = newBuf;
0266:                    }
0267:                    byte fillval = (byte) (b & 0xff);
0268:                    for (; --len >= 0;) {
0269:                        buf[size + len] = fillval;
0270:                    }
0271:                    size = newSize;
0272:                }
0273:
0274:                final void set(int b, int pos) {
0275:                    if (pos < 0)
0276:                        pos += size;
0277:                    if (pos >= 0 && pos < size)
0278:                        buf[pos] = (byte) (b & 0xff);
0279:                }
0280:
0281:                // sets last char
0282:                final void set(int b) {
0283:                    if (size > 0)
0284:                        buf[size - 1] = (byte) (b & 0xff);
0285:                }
0286:
0287:                final ByteList toByteList() {
0288:                    return new ByteList(buf, 0, size);
0289:                }
0290:
0291:                public final String toString() {
0292:                    return new String(buf, 0, size);
0293:                }
0294:            } // Buffer
0295:
0296:            // static methods only
0297:            private Sprintf() {
0298:            }
0299:
0300:            public static CharSequence sprintf(Locale locale,
0301:                    CharSequence format, IRubyObject args) {
0302:                return rubySprintf(format, new Args(locale, args));
0303:            }
0304:
0305:            public static CharSequence sprintf(CharSequence format,
0306:                    IRubyObject args) {
0307:                return rubySprintf(format, new Args(args));
0308:            }
0309:
0310:            public static CharSequence sprintf(Ruby runtime,
0311:                    CharSequence format, int arg) {
0312:                return rubySprintf(format, new Args(runtime, (long) arg));
0313:            }
0314:
0315:            public static CharSequence sprintf(Ruby runtime,
0316:                    CharSequence format, long arg) {
0317:                return rubySprintf(format, new Args(runtime, arg));
0318:            }
0319:
0320:            public static CharSequence sprintf(Locale locale,
0321:                    RubyString format, IRubyObject args) {
0322:                return rubySprintf(format.getByteList(), new Args(locale, args));
0323:            }
0324:
0325:            public static CharSequence sprintf(RubyString format,
0326:                    IRubyObject args) {
0327:                return rubySprintf(format.getByteList(), new Args(args));
0328:            }
0329:
0330:            private static CharSequence rubySprintf(CharSequence charFormat,
0331:                    Args args) {
0332:                byte[] format;
0333:                Buffer buf = new Buffer();
0334:
0335:                int offset;
0336:                int length;
0337:                int start;
0338:                int mark;
0339:
0340:                if (charFormat instanceof  ByteList) {
0341:                    ByteList list = (ByteList) charFormat;
0342:                    format = list.unsafeBytes();
0343:                    int begin = list.begin();
0344:                    offset = begin;
0345:                    length = begin + list.length();
0346:                    start = begin;
0347:                    mark = begin;
0348:                } else {
0349:                    format = stringToBytes(charFormat, false);
0350:                    offset = 0;
0351:                    length = charFormat.length();
0352:                    start = 0;
0353:                    mark = 0;
0354:                }
0355:
0356:                while (offset < length) {
0357:                    start = offset;
0358:                    for (; offset < length && format[offset] != '%'; offset++)
0359:                        ;
0360:                    if (offset > start) {
0361:                        buf.write(format, start, offset - start);
0362:                        start = offset;
0363:                    }
0364:                    if (offset++ >= length)
0365:                        break;
0366:
0367:                    IRubyObject arg = null;
0368:                    int flags = 0;
0369:                    int width = 0;
0370:                    int precision = 0;
0371:                    int number = 0;
0372:                    byte fchar = 0;
0373:                    boolean incomplete = true;
0374:                    for (; incomplete && offset < length;) {
0375:                        switch (fchar = format[offset]) {
0376:                        default:
0377:                            if (isPrintable(fchar)) {
0378:                                raiseArgumentError(args,
0379:                                        "malformed format string - %"
0380:                                                + (char) fchar);
0381:                            } else {
0382:                                raiseArgumentError(args, ERR_MALFORMED_FORMAT);
0383:                            }
0384:                            break;
0385:
0386:                        case ' ':
0387:                            flags |= FLAG_SPACE;
0388:                            offset++;
0389:                            break;
0390:                        case '0':
0391:                            flags |= FLAG_ZERO;
0392:                            offset++;
0393:                            break;
0394:                        case '+':
0395:                            flags |= FLAG_PLUS;
0396:                            offset++;
0397:                            break;
0398:                        case '-':
0399:                            flags |= FLAG_MINUS;
0400:                            offset++;
0401:                            break;
0402:                        case '#':
0403:                            flags |= FLAG_SHARP;
0404:                            offset++;
0405:                            break;
0406:                        case '1':
0407:                        case '2':
0408:                        case '3':
0409:                        case '4':
0410:                        case '5':
0411:                        case '6':
0412:                        case '7':
0413:                        case '8':
0414:                        case '9':
0415:                            // MRI doesn't flag it as an error if width is given multiple
0416:                            // times as a number (but it does for *)
0417:                            number = 0;
0418:                            for (; offset < length
0419:                                    && isDigit(fchar = format[offset]); offset++) {
0420:                                number = extendWidth(args, number, fchar);
0421:                            }
0422:                            checkOffset(args, offset, length, ERR_MALFORMED_NUM);
0423:                            if (fchar == '$') {
0424:                                if (arg != null) {
0425:                                    raiseArgumentError(args,
0426:                                            "value given twice - " + number
0427:                                                    + "$");
0428:                                }
0429:                                arg = args.getNth(number);
0430:                                offset++;
0431:                            } else {
0432:                                width = number;
0433:                                flags |= FLAG_WIDTH;
0434:                            }
0435:                            break;
0436:
0437:                        case '*':
0438:                            if ((flags & FLAG_WIDTH) != 0) {
0439:                                raiseArgumentError(args, "width given twice");
0440:                            }
0441:                            flags |= FLAG_WIDTH;
0442:                            // TODO: factor this chunk as in MRI/YARV GETASTER
0443:                            checkOffset(args, ++offset, length,
0444:                                    ERR_MALFORMED_STAR_NUM);
0445:                            mark = offset;
0446:                            number = 0;
0447:                            for (; offset < length
0448:                                    && isDigit(fchar = format[offset]); offset++) {
0449:                                number = extendWidth(args, number, fchar);
0450:                            }
0451:                            checkOffset(args, offset, length,
0452:                                    ERR_MALFORMED_STAR_NUM);
0453:                            if (fchar == '$') {
0454:                                width = args.getNthInt(number);
0455:                                if (width < 0) {
0456:                                    flags |= FLAG_MINUS;
0457:                                    width = -width;
0458:                                }
0459:                                offset++;
0460:                            } else {
0461:                                width = args.nextInt();
0462:                                if (width < 0) {
0463:                                    flags |= FLAG_MINUS;
0464:                                    width = -width;
0465:                                }
0466:                                // let the width (if any), get processed in the next loop,
0467:                                // so any leading 0 gets treated correctly 
0468:                                offset = mark;
0469:                            }
0470:                            break;
0471:
0472:                        case '.':
0473:                            if ((flags & FLAG_PRECISION) != 0) {
0474:                                raiseArgumentError(args,
0475:                                        "precision given twice");
0476:                            }
0477:                            flags |= FLAG_PRECISION;
0478:                            checkOffset(args, ++offset, length,
0479:                                    ERR_MALFORMED_DOT_NUM);
0480:                            fchar = format[offset];
0481:                            if (fchar == '*') {
0482:                                // TODO: factor this chunk as in MRI/YARV GETASTER
0483:                                checkOffset(args, ++offset, length,
0484:                                        ERR_MALFORMED_STAR_NUM);
0485:                                mark = offset;
0486:                                number = 0;
0487:                                for (; offset < length
0488:                                        && isDigit(fchar = format[offset]); offset++) {
0489:                                    number = extendWidth(args, number, fchar);
0490:                                }
0491:                                checkOffset(args, offset, length,
0492:                                        ERR_MALFORMED_STAR_NUM);
0493:                                if (fchar == '$') {
0494:                                    precision = args.getNthInt(number);
0495:                                    if (precision < 0) {
0496:                                        flags &= ~FLAG_PRECISION;
0497:                                    }
0498:                                    offset++;
0499:                                } else {
0500:                                    precision = args.nextInt();
0501:                                    if (precision < 0) {
0502:                                        flags &= ~FLAG_PRECISION;
0503:                                    }
0504:                                    // let the width (if any), get processed in the next loop,
0505:                                    // so any leading 0 gets treated correctly 
0506:                                    offset = mark;
0507:                                }
0508:                            } else {
0509:                                number = 0;
0510:                                for (; offset < length
0511:                                        && isDigit(fchar = format[offset]); offset++) {
0512:                                    number = extendWidth(args, number, fchar);
0513:                                }
0514:                                checkOffset(args, offset, length,
0515:                                        ERR_MALFORMED_DOT_NUM);
0516:                                precision = number;
0517:                            }
0518:                            break;
0519:
0520:                        case '\n':
0521:                            offset--;
0522:                        case '%':
0523:                            if (flags != FLAG_NONE) {
0524:                                raiseArgumentError(args,
0525:                                        ERR_ILLEGAL_FORMAT_CHAR);
0526:                            }
0527:                            buf.write('%');
0528:                            offset++;
0529:                            incomplete = false;
0530:                            break;
0531:
0532:                        case 'c': {
0533:                            if (arg == null)
0534:                                arg = args.next();
0535:
0536:                            int c = 0;
0537:                            // MRI 1.8.5-p12 doesn't support 1-char strings, but
0538:                            // YARV 0.4.1 does. I don't think it hurts to include
0539:                            // this; sprintf('%c','a') is nicer than sprintf('%c','a'[0])
0540:                            if (arg instanceof  RubyString) {
0541:                                ByteList bytes = ((RubyString) arg)
0542:                                        .getByteList();
0543:                                if (bytes.length() == 1) {
0544:                                    c = bytes.unsafeBytes()[bytes.begin()];
0545:                                } else {
0546:                                    raiseArgumentError(args,
0547:                                            "%c requires a character");
0548:                                }
0549:                            } else {
0550:                                c = args.intValue(arg);
0551:                            }
0552:                            if ((flags & FLAG_WIDTH) != 0 && width > 1) {
0553:                                if ((flags & FLAG_MINUS) != 0) {
0554:                                    buf.write(c);
0555:                                    buf.fill(' ', width - 1);
0556:                                } else {
0557:                                    buf.fill(' ', width - 1);
0558:                                    buf.write(c);
0559:                                }
0560:                            } else {
0561:                                buf.write(c);
0562:                            }
0563:                            offset++;
0564:                            incomplete = false;
0565:                            break;
0566:                        }
0567:                        case 'p':
0568:                        case 's': {
0569:                            if (arg == null)
0570:                                arg = args.next();
0571:
0572:                            if (fchar == 'p') {
0573:                                arg = arg.callMethod(arg.getRuntime()
0574:                                        .getCurrentContext(), "inspect");
0575:                            }
0576:                            ByteList bytes = arg.asString().getByteList();
0577:                            int len = bytes.length();
0578:                            if ((flags & FLAG_PRECISION) != 0
0579:                                    && precision < len) {
0580:                                len = precision;
0581:                            }
0582:                            // TODO: adjust length so it won't fall in the middle 
0583:                            // of a multi-byte character. MRI's sprintf.c uses tables
0584:                            // in a modified version of regex.c, which assume some
0585:                            // particular  encoding for a given installation/application.
0586:                            // (See regex.c#re_mbcinit in ruby-1.8.5-p12) 
0587:                            //
0588:                            // This is only an issue if the user specifies a precision
0589:                            // that causes the string to be truncated. The same issue
0590:                            // would arise taking a substring of a ByteList-backed RubyString.
0591:
0592:                            if ((flags & FLAG_WIDTH) != 0 && width > len) {
0593:                                width -= len;
0594:                                if ((flags & FLAG_MINUS) != 0) {
0595:                                    buf.write(bytes.unsafeBytes(), bytes
0596:                                            .begin(), len);
0597:                                    buf.fill(' ', width);
0598:                                } else {
0599:                                    buf.fill(' ', width);
0600:                                    buf.write(bytes.unsafeBytes(), bytes
0601:                                            .begin(), len);
0602:                                }
0603:                            } else {
0604:                                buf.write(bytes.unsafeBytes(), bytes.begin(),
0605:                                        len);
0606:                            }
0607:                            offset++;
0608:                            incomplete = false;
0609:                            break;
0610:                        }
0611:                        case 'd':
0612:                        case 'i':
0613:                        case 'o':
0614:                        case 'x':
0615:                        case 'X':
0616:                        case 'b':
0617:                        case 'B':
0618:                        case 'u': {
0619:                            if (arg == null)
0620:                                arg = args.next();
0621:
0622:                            int type = arg.getMetaClass().index;
0623:                            if (type != ClassIndex.FIXNUM
0624:                                    && type != ClassIndex.BIGNUM) {
0625:                                switch (type) {
0626:                                case ClassIndex.FLOAT:
0627:                                    arg = RubyNumeric.dbl2num(arg.getRuntime(),
0628:                                            ((RubyFloat) arg).getValue());
0629:                                    break;
0630:                                case ClassIndex.STRING:
0631:                                    arg = RubyNumeric.str2inum(
0632:                                            arg.getRuntime(), (RubyString) arg,
0633:                                            0, true);
0634:                                    break;
0635:                                default:
0636:                                    arg = arg.convertToType(arg.getRuntime()
0637:                                            .getClass("Integer"),
0638:                                            MethodIndex.TO_I, "to_i", true);
0639:                                    break;
0640:                                }
0641:                                type = arg.getMetaClass().index;
0642:                            }
0643:                            byte[] bytes = null;
0644:                            int first = 0;
0645:                            byte[] prefix = null;
0646:                            boolean sign;
0647:                            boolean negative;
0648:                            byte signChar = 0;
0649:                            byte leadChar = 0;
0650:                            int base;
0651:
0652:                            // 'd' and 'i' are the same
0653:                            if (fchar == 'i')
0654:                                fchar = 'd';
0655:
0656:                            // 'u' with space or plus flags is same as 'd'
0657:                            if (fchar == 'u'
0658:                                    && (flags & (FLAG_SPACE | FLAG_PLUS)) != 0) {
0659:                                fchar = 'd';
0660:                            }
0661:                            sign = (fchar == 'd' || (flags & (FLAG_SPACE | FLAG_PLUS)) != 0);
0662:
0663:                            switch (fchar) {
0664:                            case 'o':
0665:                                base = 8;
0666:                                break;
0667:                            case 'x':
0668:                            case 'X':
0669:                                base = 16;
0670:                                break;
0671:                            case 'b':
0672:                            case 'B':
0673:                                base = 2;
0674:                                break;
0675:                            case 'u':
0676:                            case 'd':
0677:                            default:
0678:                                base = 10;
0679:                                break;
0680:                            }
0681:                            if ((flags & FLAG_SHARP) != 0) {
0682:                                switch (fchar) {
0683:                                case 'o':
0684:                                    prefix = PREFIX_OCTAL;
0685:                                    break;
0686:                                case 'x':
0687:                                    prefix = PREFIX_HEX_LC;
0688:                                    break;
0689:                                case 'X':
0690:                                    prefix = PREFIX_HEX_UC;
0691:                                    break;
0692:                                case 'b':
0693:                                    prefix = PREFIX_BINARY_LC;
0694:                                    break;
0695:                                case 'B':
0696:                                    prefix = PREFIX_BINARY_UC;
0697:                                    break;
0698:                                }
0699:                                if (prefix != null)
0700:                                    width -= prefix.length;
0701:                            }
0702:                            // We depart here from strict adherence to MRI code, as MRI
0703:                            // uses C-sprintf, in part, to format numeric output, while
0704:                            // we'll use Java's numeric formatting code (and our own).
0705:                            if (type == ClassIndex.FIXNUM) {
0706:                                negative = ((RubyFixnum) arg).getLongValue() < 0;
0707:                                if (negative && fchar == 'u') {
0708:                                    bytes = getUnsignedNegativeBytes((RubyFixnum) arg);
0709:                                } else {
0710:                                    bytes = getFixnumBytes((RubyFixnum) arg,
0711:                                            base, sign, fchar == 'X');
0712:                                }
0713:                            } else {
0714:                                negative = ((RubyBignum) arg).getValue()
0715:                                        .signum() < 0;
0716:                                if (negative && fchar == 'u') {
0717:                                    bytes = getUnsignedNegativeBytes((RubyBignum) arg);
0718:                                } else {
0719:                                    bytes = getBignumBytes((RubyBignum) arg,
0720:                                            base, sign, fchar == 'X');
0721:                                }
0722:                            }
0723:                            int len = 0;
0724:                            if (sign) {
0725:                                if (negative) {
0726:                                    signChar = '-';
0727:                                    width--;
0728:                                    first = 1; // skip '-' in bytes, will add where appropriate
0729:                                } else if ((flags & FLAG_PLUS) != 0) {
0730:                                    signChar = '+';
0731:                                    width--;
0732:                                } else if ((flags & FLAG_SPACE) != 0) {
0733:                                    signChar = ' ';
0734:                                    width--;
0735:                                }
0736:                            } else if (negative) {
0737:                                if (base == 10) {
0738:                                    warning(args,
0739:                                            "negative number for %u specifier");
0740:                                    leadChar = '.';
0741:                                    len += 2;
0742:                                } else {
0743:                                    if ((flags & (FLAG_PRECISION | FLAG_ZERO)) == 0)
0744:                                        len += 2; // ..
0745:
0746:                                    first = skipSignBits(bytes, base);
0747:                                    switch (fchar) {
0748:                                    case 'b':
0749:                                    case 'B':
0750:                                        leadChar = '1';
0751:                                        break;
0752:                                    case 'o':
0753:                                        leadChar = '7';
0754:                                        break;
0755:                                    case 'x':
0756:                                        leadChar = 'f';
0757:                                        break;
0758:                                    case 'X':
0759:                                        leadChar = 'F';
0760:                                        break;
0761:                                    }
0762:                                    if (leadChar != 0)
0763:                                        len++;
0764:                                }
0765:                            }
0766:                            int numlen = bytes.length - first;
0767:                            len += numlen;
0768:
0769:                            if ((flags & (FLAG_ZERO | FLAG_PRECISION)) == FLAG_ZERO) {
0770:                                precision = width;
0771:                                width = 0;
0772:                            } else {
0773:                                if (precision < len)
0774:                                    precision = len;
0775:
0776:                                width -= precision;
0777:                            }
0778:                            if ((flags & FLAG_MINUS) == 0) {
0779:                                buf.fill(' ', width);
0780:                                width = 0;
0781:                            }
0782:                            if (signChar != 0)
0783:                                buf.write(signChar);
0784:                            if (prefix != null)
0785:                                buf.write(prefix);
0786:
0787:                            if (len < precision) {
0788:                                if (leadChar == 0) {
0789:                                    buf.fill('0', precision - len);
0790:                                } else if (leadChar == '.') {
0791:                                    buf.fill(leadChar, precision - len);
0792:                                    buf.write(PREFIX_NEGATIVE);
0793:                                } else {
0794:                                    buf.fill(leadChar, precision - len + 1); // the 1 is for the stripped sign char
0795:                                }
0796:                            } else if (leadChar != 0) {
0797:                                if ((flags & (FLAG_PRECISION | FLAG_ZERO)) == 0) {
0798:                                    buf.write(PREFIX_NEGATIVE);
0799:                                }
0800:                                if (leadChar != '.')
0801:                                    buf.write(leadChar);
0802:                            }
0803:                            buf.write(bytes, first, numlen);
0804:
0805:                            if (width > 0)
0806:                                buf.fill(' ', width);
0807:
0808:                            offset++;
0809:                            incomplete = false;
0810:                            break;
0811:                        }
0812:                        case 'E':
0813:                        case 'e':
0814:                        case 'f':
0815:                        case 'G':
0816:                        case 'g': {
0817:                            if (arg == null)
0818:                                arg = args.next();
0819:
0820:                            if (!(arg instanceof  RubyFloat)) {
0821:                                // FIXME: what is correct 'recv' argument?
0822:                                // (this does produce the desired behavior)
0823:                                arg = RubyKernel.new_float(arg, arg);
0824:                            }
0825:                            double dval = ((RubyFloat) arg).getDoubleValue();
0826:                            boolean nan = dval != dval;
0827:                            boolean inf = dval == Double.POSITIVE_INFINITY
0828:                                    || dval == Double.NEGATIVE_INFINITY;
0829:                            boolean negative = dval < 0.0d;
0830:                            byte[] digits;
0831:                            int nDigits = 0;
0832:                            int exponent = 0;
0833:
0834:                            int len = 0;
0835:                            byte signChar;
0836:
0837:                            if (nan || inf) {
0838:                                if (nan) {
0839:                                    digits = NAN_VALUE;
0840:                                    len = NAN_VALUE.length;
0841:                                } else {
0842:                                    digits = INFINITY_VALUE;
0843:                                    len = INFINITY_VALUE.length;
0844:                                }
0845:                                if (negative) {
0846:                                    signChar = '-';
0847:                                    width--;
0848:                                } else if ((flags & FLAG_PLUS) != 0) {
0849:                                    signChar = '+';
0850:                                    width--;
0851:                                } else if ((flags & FLAG_SPACE) != 0) {
0852:                                    signChar = ' ';
0853:                                    width--;
0854:                                } else {
0855:                                    signChar = 0;
0856:                                }
0857:                                width -= len;
0858:
0859:                                if (width > 0
0860:                                        && (flags & (FLAG_ZERO | FLAG_MINUS)) == 0) {
0861:                                    buf.fill(' ', width);
0862:                                    width = 0;
0863:                                }
0864:                                if (signChar != 0)
0865:                                    buf.write(signChar);
0866:
0867:                                if (width > 0 && (flags & FLAG_MINUS) == 0) {
0868:                                    buf.fill('0', width);
0869:                                    width = 0;
0870:                                }
0871:                                buf.write(digits);
0872:                                if (width > 0)
0873:                                    buf.fill(' ', width);
0874:
0875:                                offset++;
0876:                                incomplete = false;
0877:                                break;
0878:                            }
0879:                            String str = Double.toString(dval);
0880:                            // grrr, arghh, want to subclass sun.misc.FloatingDecimal, but can't,
0881:                            // so we must do all this (the next 70 lines of code), which has already
0882:                            // been done by FloatingDecimal.
0883:                            int strlen = str.length();
0884:                            digits = new byte[strlen];
0885:                            int nTrailingZeroes = 0;
0886:                            int i = negative ? 1 : 0;
0887:                            int decPos = 0;
0888:                            byte ival;
0889:                            int_loop: for (; i < strlen;) {
0890:                                switch (ival = (byte) str.charAt(i++)) {
0891:                                case '0':
0892:                                    if (nDigits > 0)
0893:                                        nTrailingZeroes++;
0894:
0895:                                    break; // switch
0896:                                case '1':
0897:                                case '2':
0898:                                case '3':
0899:                                case '4':
0900:                                case '5':
0901:                                case '6':
0902:                                case '7':
0903:                                case '8':
0904:                                case '9':
0905:                                    if (nTrailingZeroes > 0) {
0906:                                        for (; nTrailingZeroes > 0; nTrailingZeroes--) {
0907:                                            digits[nDigits++] = '0';
0908:                                        }
0909:                                    }
0910:                                    digits[nDigits++] = ival;
0911:                                    break; // switch
0912:                                case '.':
0913:                                    break int_loop;
0914:                                }
0915:                            }
0916:                            decPos = nDigits + nTrailingZeroes;
0917:                            dec_loop: for (; i < strlen;) {
0918:                                switch (ival = (byte) str.charAt(i++)) {
0919:                                case '0':
0920:                                    if (nDigits > 0) {
0921:                                        nTrailingZeroes++;
0922:                                    } else {
0923:                                        exponent--;
0924:                                    }
0925:                                    break; // switch
0926:                                case '1':
0927:                                case '2':
0928:                                case '3':
0929:                                case '4':
0930:                                case '5':
0931:                                case '6':
0932:                                case '7':
0933:                                case '8':
0934:                                case '9':
0935:                                    if (nTrailingZeroes > 0) {
0936:                                        for (; nTrailingZeroes > 0; nTrailingZeroes--) {
0937:                                            digits[nDigits++] = '0';
0938:                                        }
0939:                                    }
0940:                                    digits[nDigits++] = ival;
0941:                                    break; // switch
0942:                                case 'E':
0943:                                    break dec_loop;
0944:                                }
0945:                            }
0946:                            if (i < strlen) {
0947:                                int expSign;
0948:                                int expVal = 0;
0949:                                if (str.charAt(i) == '-') {
0950:                                    expSign = -1;
0951:                                    i++;
0952:                                } else {
0953:                                    expSign = 1;
0954:                                }
0955:                                for (; i < strlen;) {
0956:                                    expVal = expVal
0957:                                            * 10
0958:                                            + ((int) str.charAt(i++) - (int) '0');
0959:                                }
0960:                                exponent += expVal * expSign;
0961:                            }
0962:                            exponent += decPos - nDigits;
0963:
0964:                            // gotta have at least a zero...
0965:                            if (nDigits == 0) {
0966:                                digits[0] = '0';
0967:                                nDigits = 1;
0968:                                exponent = 0;
0969:                            }
0970:
0971:                            // OK, we now have the significand in digits[0...nDigits]
0972:                            // and the exponent in exponent.  We're ready to format.
0973:
0974:                            int intDigits, intZeroes, intLength;
0975:                            int decDigits, decZeroes, decLength;
0976:                            byte expChar;
0977:
0978:                            if (negative) {
0979:                                signChar = '-';
0980:                                width--;
0981:                            } else if ((flags & FLAG_PLUS) != 0) {
0982:                                signChar = '+';
0983:                                width--;
0984:                            } else if ((flags & FLAG_SPACE) != 0) {
0985:                                signChar = ' ';
0986:                                width--;
0987:                            } else {
0988:                                signChar = 0;
0989:                            }
0990:                            if ((flags & FLAG_PRECISION) == 0) {
0991:                                precision = 6;
0992:                            }
0993:
0994:                            switch (fchar) {
0995:                            case 'E':
0996:                            case 'G':
0997:                                expChar = 'E';
0998:                                break;
0999:                            case 'e':
1000:                            case 'g':
1001:                                expChar = 'e';
1002:                                break;
1003:                            default:
1004:                                expChar = 0;
1005:                            }
1006:
1007:                            switch (fchar) {
1008:                            case 'g':
1009:                            case 'G':
1010:                                // an empirically derived rule: precision applies to
1011:                                // significand length, irrespective of exponent
1012:
1013:                                // an official rule, clarified: if the exponent
1014:                                // <clarif>after adjusting for exponent form</clarif>
1015:                                // is < -4,  or the exponent <clarif>after adjusting 
1016:                                // for exponent form</clarif> is greater than the
1017:                                // precision, use exponent form
1018:                                boolean expForm = (exponent + nDigits - 1 < -4 || exponent
1019:                                        + nDigits > (precision == 0 ? 1
1020:                                        : precision));
1021:                                // it would be nice (and logical!) if exponent form 
1022:                                // behaved like E/e, and decimal form behaved like f,
1023:                                // but no such luck. hence: 
1024:                                if (expForm) {
1025:                                    // intDigits isn't used here, but if it were, it would be 1
1026:                                    /* intDigits = 1; */
1027:                                    decDigits = nDigits - 1;
1028:                                    // precision for G/g includes integer digits
1029:                                    precision = Math.max(0, precision - 1);
1030:
1031:                                    if (precision < decDigits) {
1032:                                        int n = round(digits, nDigits,
1033:                                                precision, precision != 0);
1034:                                        if (n > nDigits) {
1035:                                            nDigits = n;
1036:                                        }
1037:                                        decDigits = Math.min(nDigits - 1,
1038:                                                precision);
1039:                                    }
1040:                                    exponent += nDigits - 1;
1041:                                    if (precision > 0) {
1042:                                        len += 1 + precision; // n.prec
1043:                                    } else {
1044:                                        len += 1; // n
1045:                                        if ((flags & FLAG_SHARP) != 0) {
1046:                                            len++; // will have a trailing '.'
1047:                                        }
1048:                                    }
1049:
1050:                                    width -= len + 5; // 5 -> e+nnn / e-nnn
1051:
1052:                                    if (width > 0
1053:                                            && (flags & (FLAG_ZERO | FLAG_MINUS)) == 0) {
1054:                                        buf.fill(' ', width);
1055:                                        width = 0;
1056:                                    }
1057:                                    if (signChar != 0) {
1058:                                        buf.write(signChar);
1059:                                    }
1060:                                    if (width > 0 && (flags & FLAG_MINUS) == 0) {
1061:                                        buf.fill('0', width);
1062:                                        width = 0;
1063:                                    }
1064:                                    // now some data...
1065:                                    buf.write(digits[0]);
1066:                                    if (precision > 0) {
1067:                                        buf.write(args.getDecimalSeparator()); // '.'
1068:                                        if (decDigits > 0) {
1069:                                            buf.write(digits, 1, decDigits);
1070:                                            precision -= decDigits;
1071:                                        }
1072:                                    } else if ((flags & FLAG_SHARP) != 0) {
1073:                                        buf.write(args.getDecimalSeparator());
1074:                                    }
1075:                                    buf.write(expChar); // E or e
1076:                                    buf.write(exponent >= 0 ? '+' : '-');
1077:                                    if (exponent < 0) {
1078:                                        exponent = -exponent;
1079:                                    }
1080:                                    buf.write(exponent / 100 + '0');
1081:                                    buf.write(exponent % 100 / 10 + '0');
1082:                                    buf.write(exponent % 10 + '0');
1083:                                    if (width > 0) {
1084:                                        buf.fill(' ', width);
1085:                                    }
1086:                                } else { // decimal form, like (but not *just* like!) 'f'
1087:                                    intDigits = Math.max(0, Math.min(nDigits
1088:                                            + exponent, nDigits));
1089:                                    intZeroes = Math.max(0, exponent);
1090:                                    intLength = intDigits + intZeroes;
1091:                                    decDigits = nDigits - intDigits;
1092:                                    decZeroes = Math.max(0,
1093:                                            -(decDigits + exponent));
1094:                                    decLength = decZeroes + decDigits;
1095:                                    precision = Math.max(0, precision
1096:                                            - intLength);
1097:
1098:                                    if (precision < decDigits) {
1099:                                        int n = round(digits, nDigits,
1100:                                                intDigits + precision - 1,
1101:                                                precision != 0);
1102:                                        if (n > nDigits) {
1103:                                            // digits array shifted, update all
1104:                                            nDigits = n;
1105:                                            intDigits = Math.max(0, Math
1106:                                                    .min(nDigits + exponent,
1107:                                                            nDigits));
1108:                                            intLength = intDigits + intZeroes;
1109:                                            decDigits = nDigits - intDigits;
1110:                                            decZeroes = Math.max(0,
1111:                                                    -(decDigits + exponent));
1112:                                            precision = Math.max(0,
1113:                                                    precision - 1);
1114:                                        }
1115:                                        decDigits = precision;
1116:                                        decLength = decZeroes + decDigits;
1117:                                    }
1118:                                    len += intLength;
1119:                                    if (decLength > 0) {
1120:                                        len += decLength + 1;
1121:                                    } else {
1122:                                        if ((flags & FLAG_SHARP) != 0) {
1123:                                            len++; // will have a trailing '.'
1124:                                            if (precision > 0) { // g fills trailing zeroes if #
1125:                                                len += precision;
1126:                                            }
1127:                                        }
1128:                                    }
1129:
1130:                                    width -= len;
1131:
1132:                                    if (width > 0
1133:                                            && (flags & (FLAG_ZERO | FLAG_MINUS)) == 0) {
1134:                                        buf.fill(' ', width);
1135:                                        width = 0;
1136:                                    }
1137:                                    if (signChar != 0) {
1138:                                        buf.write(signChar);
1139:                                    }
1140:                                    if (width > 0 && (flags & FLAG_MINUS) == 0) {
1141:                                        buf.fill('0', width);
1142:                                        width = 0;
1143:                                    }
1144:                                    // now some data...
1145:                                    if (intLength > 0) {
1146:                                        if (intDigits > 0) { // s/b true, since intLength > 0
1147:                                            buf.write(digits, 0, intDigits);
1148:                                        }
1149:                                        if (intZeroes > 0) {
1150:                                            buf.fill('0', intZeroes);
1151:                                        }
1152:                                    } else {
1153:                                        // always need at least a 0
1154:                                        buf.write('0');
1155:                                    }
1156:                                    if (decLength > 0
1157:                                            || (flags & FLAG_SHARP) != 0) {
1158:                                        buf.write(args.getDecimalSeparator());
1159:                                    }
1160:                                    if (decLength > 0) {
1161:                                        if (decZeroes > 0) {
1162:                                            buf.fill('0', decZeroes);
1163:                                            precision -= decZeroes;
1164:                                        }
1165:                                        if (decDigits > 0) {
1166:                                            buf.write(digits, intDigits,
1167:                                                    decDigits);
1168:                                            precision -= decDigits;
1169:                                        }
1170:                                        if ((flags & FLAG_SHARP) != 0
1171:                                                && precision > 0) {
1172:                                            buf.fill('0', precision);
1173:                                        }
1174:                                    }
1175:                                    if ((flags & FLAG_SHARP) != 0
1176:                                            && precision > 0) {
1177:                                        buf.fill('0', precision);
1178:                                    }
1179:                                    if (width > 0) {
1180:                                        buf.fill(' ', width);
1181:                                    }
1182:                                }
1183:                                break;
1184:
1185:                            case 'f':
1186:                                intDigits = Math.max(0, Math.min(nDigits
1187:                                        + exponent, nDigits));
1188:                                intZeroes = Math.max(0, exponent);
1189:                                intLength = intDigits + intZeroes;
1190:                                decDigits = nDigits - intDigits;
1191:                                decZeroes = Math
1192:                                        .max(0, -(decDigits + exponent));
1193:                                decLength = decZeroes + decDigits;
1194:
1195:                                if (precision < decLength) {
1196:                                    if (precision < decZeroes) {
1197:                                        decDigits = 0;
1198:                                        decZeroes = precision;
1199:                                    } else {
1200:                                        int n = round(digits, nDigits,
1201:                                                intDigits + precision
1202:                                                        - decZeroes - 1,
1203:                                                precision != 0);
1204:                                        if (n > nDigits) {
1205:                                            // digits arr shifted, update all
1206:                                            nDigits = n;
1207:                                            intDigits = Math.max(0, Math
1208:                                                    .min(nDigits + exponent,
1209:                                                            nDigits));
1210:                                            intLength = intDigits + intZeroes;
1211:                                            decDigits = nDigits - intDigits;
1212:                                            decZeroes = Math.max(0,
1213:                                                    -(decDigits + exponent));
1214:                                            decLength = decZeroes + decDigits;
1215:                                        }
1216:                                        decDigits = precision - decZeroes;
1217:                                    }
1218:                                    decLength = decZeroes + decDigits;
1219:                                }
1220:                                if (precision > 0) {
1221:                                    len += Math.max(1, intLength) + 1
1222:                                            + precision;
1223:                                    // (1|intlen).prec
1224:                                } else {
1225:                                    len += Math.max(1, intLength);
1226:                                    // (1|intlen)
1227:                                    if ((flags & FLAG_SHARP) != 0) {
1228:                                        len++; // will have a trailing '.'
1229:                                    }
1230:                                }
1231:
1232:                                width -= len;
1233:
1234:                                if (width > 0
1235:                                        && (flags & (FLAG_ZERO | FLAG_MINUS)) == 0) {
1236:                                    buf.fill(' ', width);
1237:                                    width = 0;
1238:                                }
1239:                                if (signChar != 0) {
1240:                                    buf.write(signChar);
1241:                                }
1242:                                if (width > 0 && (flags & FLAG_MINUS) == 0) {
1243:                                    buf.fill('0', width);
1244:                                    width = 0;
1245:                                }
1246:                                // now some data...
1247:                                if (intLength > 0) {
1248:                                    if (intDigits > 0) { // s/b true, since intLength > 0
1249:                                        buf.write(digits, 0, intDigits);
1250:                                    }
1251:                                    if (intZeroes > 0) {
1252:                                        buf.fill('0', intZeroes);
1253:                                    }
1254:                                } else {
1255:                                    // always need at least a 0
1256:                                    buf.write('0');
1257:                                }
1258:                                if (precision > 0 || (flags & FLAG_SHARP) != 0) {
1259:                                    buf.write(args.getDecimalSeparator());
1260:                                }
1261:                                if (precision > 0) {
1262:                                    if (decZeroes > 0) {
1263:                                        buf.fill('0', decZeroes);
1264:                                        precision -= decZeroes;
1265:                                    }
1266:                                    if (decDigits > 0) {
1267:                                        buf.write(digits, intDigits, decDigits);
1268:                                        precision -= decDigits;
1269:                                    }
1270:                                    // fill up the rest with zeroes
1271:                                    if (precision > 0) {
1272:                                        buf.fill('0', precision);
1273:                                    }
1274:                                }
1275:                                if (width > 0) {
1276:                                    buf.fill(' ', width);
1277:                                }
1278:                                break;
1279:                            case 'E':
1280:                            case 'e':
1281:                                // intDigits isn't used here, but if it were, it would be 1
1282:                                /* intDigits = 1; */
1283:                                decDigits = nDigits - 1;
1284:
1285:                                if (precision < decDigits) {
1286:                                    int n = round(digits, nDigits, precision,
1287:                                            precision != 0);
1288:                                    if (n > nDigits) {
1289:                                        nDigits = n;
1290:                                    }
1291:                                    decDigits = Math
1292:                                            .min(nDigits - 1, precision);
1293:                                }
1294:                                exponent += nDigits - 1;
1295:                                if (precision > 0) {
1296:                                    len += 2 + precision; // n.prec
1297:                                } else {
1298:                                    len += 1; // n
1299:                                    if ((flags & FLAG_SHARP) != 0) {
1300:                                        len++; // will have a trailing '.'
1301:                                    }
1302:                                }
1303:
1304:                                width -= len + 5; // 5 -> e+nnn / e-nnn
1305:
1306:                                if (width > 0
1307:                                        && (flags & (FLAG_ZERO | FLAG_MINUS)) == 0) {
1308:                                    buf.fill(' ', width);
1309:                                    width = 0;
1310:                                }
1311:                                if (signChar != 0) {
1312:                                    buf.write(signChar);
1313:                                }
1314:                                if (width > 0 && (flags & FLAG_MINUS) == 0) {
1315:                                    buf.fill('0', width);
1316:                                    width = 0;
1317:                                }
1318:                                // now some data...
1319:                                buf.write(digits[0]);
1320:                                if (precision > 0) {
1321:                                    buf.write(args.getDecimalSeparator()); // '.'
1322:                                    if (decDigits > 0) {
1323:                                        buf.write(digits, 1, decDigits);
1324:                                        precision -= decDigits;
1325:                                    }
1326:                                    if (precision > 0) {
1327:                                        buf.fill('0', precision);
1328:                                    }
1329:
1330:                                } else if ((flags & FLAG_SHARP) != 0) {
1331:                                    buf.write(args.getDecimalSeparator());
1332:                                }
1333:                                buf.write(expChar); // E or e
1334:                                buf.write(exponent >= 0 ? '+' : '-');
1335:                                if (exponent < 0) {
1336:                                    exponent = -exponent;
1337:                                }
1338:                                buf.write(exponent / 100 + '0');
1339:                                buf.write(exponent % 100 / 10 + '0');
1340:                                buf.write(exponent % 10 + '0');
1341:                                if (width > 0) {
1342:                                    buf.fill(' ', width);
1343:                                }
1344:                                break;
1345:                            } // switch (format char E,e,f,G,g)
1346:
1347:                            offset++;
1348:                            incomplete = false;
1349:                            break;
1350:                        } // block (case E,e,f,G,g)
1351:                        } // switch (each format char in spec)
1352:                    } // for (each format spec)
1353:
1354:                    // equivalent to MRI case '\0':
1355:                    if (incomplete) {
1356:                        if (flags == FLAG_NONE) {
1357:                            // dangling '%' char
1358:                            buf.write('%');
1359:                        } else {
1360:                            raiseArgumentError(args, ERR_ILLEGAL_FORMAT_CHAR);
1361:                        }
1362:                    }
1363:                } // main while loop (offset < length)
1364:
1365:                return buf.toByteList();
1366:            }
1367:
1368:            // debugging code, keeping for now
1369:            /*
1370:            private static final void showLiteral(byte[] format, int start, int offset) {
1371:                System.out.println("literal: ["+ new String(format,start,offset-start)+ "], " +
1372:                        " s="+ start + " o="+ offset);
1373:            }
1374:            
1375:            // debugging code, keeping for now
1376:            private static final void showVals(byte[] format,int start,int offset, byte fchar,
1377:                    int flags, int width, int precision, Object arg) {
1378:                System.out.println(new StringBuffer()
1379:                .append("value: ").append(new String(format,start,offset-start+1)).append('\n')
1380:                .append("type: ").append((char)fchar).append('\n')
1381:                .append("start: ").append(start).append('\n')
1382:                .append("length: ").append(offset-start).append('\n')
1383:                .append("flags: ").append(Integer.toBinaryString(flags)).append('\n')
1384:                .append("width: ").append(width).append('\n')
1385:                .append("precision: ").append(precision).append('\n')
1386:                .append("arg: ").append(arg).append('\n')
1387:                .toString());
1388:                
1389:            }
1390:             */
1391:
1392:            private static final void raiseArgumentError(Args args,
1393:                    String message) {
1394:                args.raiseArgumentError(message);
1395:            }
1396:
1397:            private static final void warning(Args args, String message) {
1398:                args.warning(message);
1399:            }
1400:
1401:            private static final void checkOffset(Args args, int offset,
1402:                    int length, String message) {
1403:                if (offset >= length) {
1404:                    raiseArgumentError(args, message);
1405:                }
1406:            }
1407:
1408:            private static final int extendWidth(Args args, int oldWidth,
1409:                    byte newChar) {
1410:                int newWidth = oldWidth * 10 + (newChar - '0');
1411:                if (newWidth / 10 != oldWidth) {
1412:                    raiseArgumentError(args, "width too big");
1413:                }
1414:                return newWidth;
1415:            }
1416:
1417:            private static final boolean isDigit(byte aChar) {
1418:                return (aChar >= '0' && aChar <= '9');
1419:            }
1420:
1421:            private static final boolean isPrintable(byte aChar) {
1422:                return (aChar > 32 && aChar < 127);
1423:            }
1424:
1425:            private static final int skipSignBits(byte[] bytes, int base) {
1426:                int skip = 0;
1427:                int length = bytes.length;
1428:                byte b;
1429:                switch (base) {
1430:                case 2:
1431:                    for (; skip < length && bytes[skip] == '1'; skip++)
1432:                        ;
1433:                    break;
1434:                case 8:
1435:                    if (length > 0 && bytes[0] == '3') {
1436:                        skip++;
1437:                    }
1438:                    for (; skip < length && bytes[skip] == '7'; skip++)
1439:                        ;
1440:                    break;
1441:                case 10:
1442:                    if (length > 0 && bytes[0] == '-') {
1443:                        skip++;
1444:                    }
1445:                    break;
1446:                case 16:
1447:                    for (; skip < length
1448:                            && ((b = bytes[skip]) == 'f' || b == 'F'); skip++)
1449:                        ;
1450:                }
1451:                return skip;
1452:            }
1453:
1454:            private static final int round(byte[] bytes, int nDigits,
1455:                    int roundPos, boolean roundDown) {
1456:                int next = roundPos + 1;
1457:                if (next >= nDigits || bytes[next] < '5' ||
1458:                // MRI rounds up on nnn5nnn, but not nnn5 --
1459:                        // except for when they do
1460:                        (roundDown && bytes[next] == '5' && next == nDigits - 1)) {
1461:                    return nDigits;
1462:                }
1463:                if (roundPos < 0) { // "%.0f" % 0.99
1464:                    System.arraycopy(bytes, 0, bytes, 1, nDigits);
1465:                    bytes[0] = '1';
1466:                    return nDigits + 1;
1467:                }
1468:                bytes[roundPos] += 1;
1469:                while (bytes[roundPos] > '9') {
1470:                    bytes[roundPos] = '0';
1471:                    roundPos--;
1472:                    if (roundPos >= 0) {
1473:                        bytes[roundPos] += 1;
1474:                    } else {
1475:                        System.arraycopy(bytes, 0, bytes, 1, nDigits);
1476:                        bytes[0] = '1';
1477:                        return nDigits + 1;
1478:                    }
1479:                }
1480:                return nDigits;
1481:            }
1482:
1483:            private static final byte[] getFixnumBytes(RubyFixnum arg,
1484:                    int base, boolean sign, boolean upper) {
1485:                long val = arg.getLongValue();
1486:
1487:                // limit the length of negatives if possible (also faster)
1488:                if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) {
1489:                    if (sign) {
1490:                        return Convert.intToByteArray((int) val, base, upper);
1491:                    } else {
1492:                        switch (base) {
1493:                        case 2:
1494:                            return Convert.intToBinaryBytes((int) val);
1495:                        case 8:
1496:                            return Convert.intToOctalBytes((int) val);
1497:                        case 10:
1498:                        default:
1499:                            return Convert.intToCharBytes((int) val);
1500:                        case 16:
1501:                            return Convert.intToHexBytes((int) val, upper);
1502:                        }
1503:                    }
1504:                } else {
1505:                    if (sign) {
1506:                        return Convert.longToByteArray(val, base, upper);
1507:                    } else {
1508:                        switch (base) {
1509:                        case 2:
1510:                            return Convert.longToBinaryBytes(val);
1511:                        case 8:
1512:                            return Convert.longToOctalBytes(val);
1513:                        case 10:
1514:                        default:
1515:                            return Convert.longToCharBytes(val);
1516:                        case 16:
1517:                            return Convert.longToHexBytes(val, upper);
1518:                        }
1519:                    }
1520:                }
1521:            }
1522:
1523:            private static final byte[] getBignumBytes(RubyBignum arg,
1524:                    int base, boolean sign, boolean upper) {
1525:                BigInteger val = arg.getValue();
1526:                if (sign || base == 10 || val.signum() >= 0) {
1527:                    return stringToBytes(val.toString(base), upper);
1528:                }
1529:
1530:                // negative values
1531:                byte[] bytes = val.toByteArray();
1532:                switch (base) {
1533:                case 2:
1534:                    return Convert.twosComplementToBinaryBytes(bytes);
1535:                case 8:
1536:                    return Convert.twosComplementToOctalBytes(bytes);
1537:                case 16:
1538:                    return Convert.twosComplementToHexBytes(bytes, upper);
1539:                default:
1540:                    return stringToBytes(val.toString(base), upper);
1541:                }
1542:            }
1543:
1544:            private static final byte[] getUnsignedNegativeBytes(RubyInteger arg) {
1545:                // calculation for negatives when %u specified
1546:                // for values >= Integer.MIN_VALUE * 2, MRI uses (the equivalent of)
1547:                //   long neg_u = (((long)Integer.MAX_VALUE + 1) << 1) + val
1548:                // for smaller values, BigInteger math is required to conform to MRI's
1549:                // result.
1550:                long longval;
1551:                BigInteger bigval;
1552:
1553:                if (arg instanceof  RubyFixnum) {
1554:                    // relatively cheap test for 32-bit values
1555:                    longval = ((RubyFixnum) arg).getLongValue();
1556:                    if (longval >= (long) Integer.MIN_VALUE << 1) {
1557:                        return Convert
1558:                                .longToCharBytes((((long) Integer.MAX_VALUE + 1L) << 1)
1559:                                        + longval);
1560:                    }
1561:                    // no such luck...
1562:                    bigval = BigInteger.valueOf(longval);
1563:                } else {
1564:                    bigval = ((RubyBignum) arg).getValue();
1565:                }
1566:                // ok, now it gets expensive...
1567:                int shift = 0;
1568:                // go through negated powers of 32 until we find one small enough 
1569:                for (BigInteger minus = BIG_MINUS_64; bigval.compareTo(minus) < 0; minus = minus
1570:                        .shiftLeft(32), shift++)
1571:                    ;
1572:                // add to the corresponding positive power of 32 for the result.
1573:                // meaningful? no. conformant? yes. I just write the code...
1574:                BigInteger nPower32 = shift > 0 ? BIG_64.shiftLeft(32 * shift)
1575:                        : BIG_64;
1576:                return stringToBytes(nPower32.add(bigval).toString(), false);
1577:            }
1578:
1579:            private static final byte[] stringToBytes(CharSequence s,
1580:                    boolean upper) {
1581:                int len = s.length();
1582:                byte[] bytes = new byte[len];
1583:                if (upper) {
1584:                    for (int i = len; --i >= 0;) {
1585:                        int b = (byte) ((int) s.charAt(i) & (int) 0xff);
1586:                        if (b >= 'a' && b <= 'z') {
1587:                            bytes[i] = (byte) (b & ~0x20);
1588:                        } else {
1589:                            bytes[i] = (byte) b;
1590:                        }
1591:                    }
1592:                } else {
1593:                    for (int i = len; --i >= 0;) {
1594:                        bytes[i] = (byte) ((int) s.charAt(i) & (int) 0xff);
1595:                    }
1596:                }
1597:                return bytes;
1598:            }
1599:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.