Source Code Cross Referenced for DateTimeFormatterBuilder.java in  » Development » Joda-Time » org » joda » time » format » 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 » Development » Joda Time » org.joda.time.format 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *  Copyright 2001-2005 Stephen Colebourne
0003:         *
0004:         *  Licensed under the Apache License, Version 2.0 (the "License");
0005:         *  you may not use this file except in compliance with the License.
0006:         *  You may obtain a copy of the License at
0007:         *
0008:         *      http://www.apache.org/licenses/LICENSE-2.0
0009:         *
0010:         *  Unless required by applicable law or agreed to in writing, software
0011:         *  distributed under the License is distributed on an "AS IS" BASIS,
0012:         *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013:         *  See the License for the specific language governing permissions and
0014:         *  limitations under the License.
0015:         */
0016:        package org.joda.time.format;
0017:
0018:        import java.io.IOException;
0019:        import java.io.Writer;
0020:        import java.util.ArrayList;
0021:        import java.util.HashMap;
0022:        import java.util.HashSet;
0023:        import java.util.List;
0024:        import java.util.Locale;
0025:        import java.util.Map;
0026:        import java.util.Set;
0027:
0028:        import org.joda.time.Chronology;
0029:        import org.joda.time.DateTimeConstants;
0030:        import org.joda.time.DateTimeField;
0031:        import org.joda.time.DateTimeFieldType;
0032:        import org.joda.time.DateTimeZone;
0033:        import org.joda.time.MutableDateTime;
0034:        import org.joda.time.ReadablePartial;
0035:        import org.joda.time.MutableDateTime.Property;
0036:        import org.joda.time.field.MillisDurationField;
0037:        import org.joda.time.field.PreciseDateTimeField;
0038:
0039:        /**
0040:         * Factory that creates complex instances of DateTimeFormatter via method calls.
0041:         * <p>
0042:         * Datetime formatting is performed by the {@link DateTimeFormatter} class.
0043:         * Three classes provide factory methods to create formatters, and this is one.
0044:         * The others are {@link DateTimeFormat} and {@link ISODateTimeFormat}.
0045:         * <p>
0046:         * DateTimeFormatterBuilder is used for constructing formatters which are then
0047:         * used to print or parse. The formatters are built by appending specific fields
0048:         * or other formatters to an instance of this builder.
0049:         * <p>
0050:         * For example, a formatter that prints month and year, like "January 1970",
0051:         * can be constructed as follows:
0052:         * <p>
0053:         * <pre>
0054:         * DateTimeFormatter monthAndYear = new DateTimeFormatterBuilder()
0055:         *     .appendMonthOfYearText()
0056:         *     .appendLiteral(' ')
0057:         *     .appendYear(4, 4)
0058:         *     .toFormatter();
0059:         * </pre>
0060:         * <p>
0061:         * DateTimeFormatterBuilder itself is mutable and not thread-safe, but the
0062:         * formatters that it builds are thread-safe and immutable.
0063:         *
0064:         * @author Brian S O'Neill
0065:         * @author Stephen Colebourne
0066:         * @author Fredrik Borgh
0067:         * @since 1.0
0068:         * @see DateTimeFormat
0069:         * @see ISODateTimeFormat
0070:         */
0071:        public class DateTimeFormatterBuilder {
0072:
0073:            /** Array of printers and parsers (alternating). */
0074:            private ArrayList iElementPairs;
0075:            /** Cache of the last returned formatter. */
0076:            private Object iFormatter;
0077:
0078:            //-----------------------------------------------------------------------
0079:            /**
0080:             * Creates a DateTimeFormatterBuilder.
0081:             */
0082:            public DateTimeFormatterBuilder() {
0083:                super ();
0084:                iElementPairs = new ArrayList();
0085:            }
0086:
0087:            //-----------------------------------------------------------------------
0088:            /**
0089:             * Constructs a DateTimeFormatter using all the appended elements.
0090:             * <p>
0091:             * This is the main method used by applications at the end of the build
0092:             * process to create a usable formatter.
0093:             * <p>
0094:             * Subsequent changes to this builder do not affect the returned formatter.
0095:             * <p>
0096:             * The returned formatter may not support both printing and parsing.
0097:             * The methods {@link DateTimeFormatter#isPrinter()} and
0098:             * {@link DateTimeFormatter#isParser()} will help you determine the state
0099:             * of the formatter.
0100:             *
0101:             * @throws UnsupportedOperationException if neither printing nor parsing is supported
0102:             */
0103:            public DateTimeFormatter toFormatter() {
0104:                Object f = getFormatter();
0105:                DateTimePrinter printer = null;
0106:                if (isPrinter(f)) {
0107:                    printer = (DateTimePrinter) f;
0108:                }
0109:                DateTimeParser parser = null;
0110:                if (isParser(f)) {
0111:                    parser = (DateTimeParser) f;
0112:                }
0113:                if (printer != null || parser != null) {
0114:                    return new DateTimeFormatter(printer, parser);
0115:                }
0116:                throw new UnsupportedOperationException(
0117:                        "Both printing and parsing not supported");
0118:            }
0119:
0120:            /**
0121:             * Internal method to create a DateTimePrinter instance using all the
0122:             * appended elements.
0123:             * <p>
0124:             * Most applications will not use this method.
0125:             * If you want a printer in an application, call {@link #toFormatter()}
0126:             * and just use the printing API.
0127:             * <p>
0128:             * Subsequent changes to this builder do not affect the returned printer.
0129:             *
0130:             * @throws UnsupportedOperationException if printing is not supported
0131:             */
0132:            public DateTimePrinter toPrinter() {
0133:                Object f = getFormatter();
0134:                if (isPrinter(f)) {
0135:                    return (DateTimePrinter) f;
0136:                }
0137:                throw new UnsupportedOperationException(
0138:                        "Printing is not supported");
0139:            }
0140:
0141:            /**
0142:             * Internal method to create a DateTimeParser instance using all the
0143:             * appended elements.
0144:             * <p>
0145:             * Most applications will not use this method.
0146:             * If you want a parser in an application, call {@link #toFormatter()}
0147:             * and just use the parsing API.
0148:             * <p>
0149:             * Subsequent changes to this builder do not affect the returned parser.
0150:             *
0151:             * @throws UnsupportedOperationException if parsing is not supported
0152:             */
0153:            public DateTimeParser toParser() {
0154:                Object f = getFormatter();
0155:                if (isParser(f)) {
0156:                    return (DateTimeParser) f;
0157:                }
0158:                throw new UnsupportedOperationException(
0159:                        "Parsing is not supported");
0160:            }
0161:
0162:            //-----------------------------------------------------------------------
0163:            /**
0164:             * Returns true if toFormatter can be called without throwing an
0165:             * UnsupportedOperationException.
0166:             * 
0167:             * @return true if a formatter can be built
0168:             */
0169:            public boolean canBuildFormatter() {
0170:                return isFormatter(getFormatter());
0171:            }
0172:
0173:            /**
0174:             * Returns true if toPrinter can be called without throwing an
0175:             * UnsupportedOperationException.
0176:             * 
0177:             * @return true if a printer can be built
0178:             */
0179:            public boolean canBuildPrinter() {
0180:                return isPrinter(getFormatter());
0181:            }
0182:
0183:            /**
0184:             * Returns true if toParser can be called without throwing an
0185:             * UnsupportedOperationException.
0186:             * 
0187:             * @return true if a parser can be built
0188:             */
0189:            public boolean canBuildParser() {
0190:                return isParser(getFormatter());
0191:            }
0192:
0193:            //-----------------------------------------------------------------------
0194:            /**
0195:             * Clears out all the appended elements, allowing this builder to be
0196:             * reused.
0197:             */
0198:            public void clear() {
0199:                iFormatter = null;
0200:                iElementPairs.clear();
0201:            }
0202:
0203:            //-----------------------------------------------------------------------
0204:            /**
0205:             * Appends another formatter.
0206:             *
0207:             * @param formatter  the formatter to add
0208:             * @return this DateTimeFormatterBuilder
0209:             * @throws IllegalArgumentException if formatter is null or of an invalid type
0210:             */
0211:            public DateTimeFormatterBuilder append(DateTimeFormatter formatter) {
0212:                if (formatter == null) {
0213:                    throw new IllegalArgumentException("No formatter supplied");
0214:                }
0215:                return append0(formatter.getPrinter(), formatter.getParser());
0216:            }
0217:
0218:            /**
0219:             * Appends just a printer. With no matching parser, a parser cannot be
0220:             * built from this DateTimeFormatterBuilder.
0221:             *
0222:             * @param printer  the printer to add
0223:             * @return this DateTimeFormatterBuilder
0224:             * @throws IllegalArgumentException if printer is null or of an invalid type
0225:             */
0226:            public DateTimeFormatterBuilder append(DateTimePrinter printer) {
0227:                checkPrinter(printer);
0228:                return append0(printer, null);
0229:            }
0230:
0231:            /**
0232:             * Appends just a parser. With no matching printer, a printer cannot be
0233:             * built from this builder.
0234:             *
0235:             * @param parser  the parser to add
0236:             * @return this DateTimeFormatterBuilder
0237:             * @throws IllegalArgumentException if parser is null or of an invalid type
0238:             */
0239:            public DateTimeFormatterBuilder append(DateTimeParser parser) {
0240:                checkParser(parser);
0241:                return append0(null, parser);
0242:            }
0243:
0244:            /**
0245:             * Appends a printer/parser pair.
0246:             *
0247:             * @param printer  the printer to add
0248:             * @param parser  the parser to add
0249:             * @return this DateTimeFormatterBuilder
0250:             * @throws IllegalArgumentException if printer or parser is null or of an invalid type
0251:             */
0252:            public DateTimeFormatterBuilder append(DateTimePrinter printer,
0253:                    DateTimeParser parser) {
0254:                checkPrinter(printer);
0255:                checkParser(parser);
0256:                return append0(printer, parser);
0257:            }
0258:
0259:            /**
0260:             * Appends a printer and a set of matching parsers. When parsing, the first
0261:             * parser in the list is selected for parsing. If it fails, the next is
0262:             * chosen, and so on. If none of these parsers succeeds, then the failed
0263:             * position of the parser that made the greatest progress is returned.
0264:             * <p>
0265:             * Only the printer is optional. In addtion, it is illegal for any but the
0266:             * last of the parser array elements to be null. If the last element is
0267:             * null, this represents the empty parser. The presence of an empty parser
0268:             * indicates that the entire array of parse formats is optional.
0269:             *
0270:             * @param printer  the printer to add
0271:             * @param parsers  the parsers to add
0272:             * @return this DateTimeFormatterBuilder
0273:             * @throws IllegalArgumentException if any printer or parser is of an invalid type
0274:             * @throws IllegalArgumentException if any parser element but the last is null
0275:             */
0276:            public DateTimeFormatterBuilder append(DateTimePrinter printer,
0277:                    DateTimeParser[] parsers) {
0278:                if (printer != null) {
0279:                    checkPrinter(printer);
0280:                }
0281:                if (parsers == null) {
0282:                    throw new IllegalArgumentException("No parsers supplied");
0283:                }
0284:                int length = parsers.length;
0285:                if (length == 1) {
0286:                    if (parsers[0] == null) {
0287:                        throw new IllegalArgumentException("No parser supplied");
0288:                    }
0289:                    return append0(printer, parsers[0]);
0290:                }
0291:
0292:                DateTimeParser[] copyOfParsers = new DateTimeParser[length];
0293:                int i;
0294:                for (i = 0; i < length - 1; i++) {
0295:                    if ((copyOfParsers[i] = parsers[i]) == null) {
0296:                        throw new IllegalArgumentException(
0297:                                "Incomplete parser array");
0298:                    }
0299:                }
0300:                copyOfParsers[i] = parsers[i];
0301:
0302:                return append0(printer, new MatchingParser(copyOfParsers));
0303:            }
0304:
0305:            /**
0306:             * Appends just a parser element which is optional. With no matching
0307:             * printer, a printer cannot be built from this DateTimeFormatterBuilder.
0308:             *
0309:             * @return this DateTimeFormatterBuilder
0310:             * @throws IllegalArgumentException if parser is null or of an invalid type
0311:             */
0312:            public DateTimeFormatterBuilder appendOptional(DateTimeParser parser) {
0313:                checkParser(parser);
0314:                DateTimeParser[] parsers = new DateTimeParser[] { parser, null };
0315:                return append0(null, new MatchingParser(parsers));
0316:            }
0317:
0318:            //-----------------------------------------------------------------------
0319:            /**
0320:             * Checks if the parser is non null and a provider.
0321:             * 
0322:             * @param parser  the parser to check
0323:             */
0324:            private void checkParser(DateTimeParser parser) {
0325:                if (parser == null) {
0326:                    throw new IllegalArgumentException("No parser supplied");
0327:                }
0328:            }
0329:
0330:            /**
0331:             * Checks if the printer is non null and a provider.
0332:             * 
0333:             * @param printer  the printer to check
0334:             */
0335:            private void checkPrinter(DateTimePrinter printer) {
0336:                if (printer == null) {
0337:                    throw new IllegalArgumentException("No printer supplied");
0338:                }
0339:            }
0340:
0341:            private DateTimeFormatterBuilder append0(Object element) {
0342:                iFormatter = null;
0343:                // Add the element as both a printer and parser.
0344:                iElementPairs.add(element);
0345:                iElementPairs.add(element);
0346:                return this ;
0347:            }
0348:
0349:            private DateTimeFormatterBuilder append0(DateTimePrinter printer,
0350:                    DateTimeParser parser) {
0351:                iFormatter = null;
0352:                iElementPairs.add(printer);
0353:                iElementPairs.add(parser);
0354:                return this ;
0355:            }
0356:
0357:            //-----------------------------------------------------------------------
0358:            /**
0359:             * Instructs the printer to emit a specific character, and the parser to
0360:             * expect it. The parser is case-insensitive.
0361:             *
0362:             * @return this DateTimeFormatterBuilder
0363:             */
0364:            public DateTimeFormatterBuilder appendLiteral(char c) {
0365:                return append0(new CharacterLiteral(c));
0366:            }
0367:
0368:            /**
0369:             * Instructs the printer to emit specific text, and the parser to expect
0370:             * it. The parser is case-insensitive.
0371:             *
0372:             * @return this DateTimeFormatterBuilder
0373:             * @throws IllegalArgumentException if text is null
0374:             */
0375:            public DateTimeFormatterBuilder appendLiteral(String text) {
0376:                if (text == null) {
0377:                    throw new IllegalArgumentException(
0378:                            "Literal must not be null");
0379:                }
0380:                switch (text.length()) {
0381:                case 0:
0382:                    return this ;
0383:                case 1:
0384:                    return append0(new CharacterLiteral(text.charAt(0)));
0385:                default:
0386:                    return append0(new StringLiteral(text));
0387:                }
0388:            }
0389:
0390:            /**
0391:             * Instructs the printer to emit a field value as a decimal number, and the
0392:             * parser to expect an unsigned decimal number.
0393:             *
0394:             * @param fieldType type of field to append
0395:             * @param minDigits minumum number of digits to <i>print</i>
0396:             * @param maxDigits maximum number of digits to <i>parse</i>, or the estimated
0397:             * maximum number of digits to print
0398:             * @return this DateTimeFormatterBuilder
0399:             * @throws IllegalArgumentException if field type is null
0400:             */
0401:            public DateTimeFormatterBuilder appendDecimal(
0402:                    DateTimeFieldType fieldType, int minDigits, int maxDigits) {
0403:                if (fieldType == null) {
0404:                    throw new IllegalArgumentException(
0405:                            "Field type must not be null");
0406:                }
0407:                if (maxDigits < minDigits) {
0408:                    maxDigits = minDigits;
0409:                }
0410:                if (minDigits < 0 || maxDigits <= 0) {
0411:                    throw new IllegalArgumentException();
0412:                }
0413:                if (minDigits <= 1) {
0414:                    return append0(new UnpaddedNumber(fieldType, maxDigits,
0415:                            false));
0416:                } else {
0417:                    return append0(new PaddedNumber(fieldType, maxDigits,
0418:                            false, minDigits));
0419:                }
0420:            }
0421:
0422:            /**
0423:             * Instructs the printer to emit a field value as a fixed-width decimal
0424:             * number (smaller numbers will be left-padded with zeros), and the parser
0425:             * to expect an unsigned decimal number with the same fixed width.
0426:             * 
0427:             * @param fieldType type of field to append
0428:             * @param numDigits the exact number of digits to parse or print, except if
0429:             * printed value requires more digits
0430:             * @return this DateTimeFormatterBuilder
0431:             * @throws IllegalArgumentException if field type is null or if <code>numDigits <= 0</code>
0432:             * @since 1.5
0433:             */
0434:            public DateTimeFormatterBuilder appendFixedDecimal(
0435:                    DateTimeFieldType fieldType, int numDigits) {
0436:                if (fieldType == null) {
0437:                    throw new IllegalArgumentException(
0438:                            "Field type must not be null");
0439:                }
0440:                if (numDigits <= 0) {
0441:                    throw new IllegalArgumentException(
0442:                            "Illegal number of digits: " + numDigits);
0443:                }
0444:                return append0(new FixedNumber(fieldType, numDigits, false));
0445:            }
0446:
0447:            /**
0448:             * Instructs the printer to emit a field value as a decimal number, and the
0449:             * parser to expect a signed decimal number.
0450:             *
0451:             * @param fieldType type of field to append
0452:             * @param minDigits minumum number of digits to <i>print</i>
0453:             * @param maxDigits maximum number of digits to <i>parse</i>, or the estimated
0454:             * maximum number of digits to print
0455:             * @return this DateTimeFormatterBuilder
0456:             * @throws IllegalArgumentException if field type is null
0457:             */
0458:            public DateTimeFormatterBuilder appendSignedDecimal(
0459:                    DateTimeFieldType fieldType, int minDigits, int maxDigits) {
0460:                if (fieldType == null) {
0461:                    throw new IllegalArgumentException(
0462:                            "Field type must not be null");
0463:                }
0464:                if (maxDigits < minDigits) {
0465:                    maxDigits = minDigits;
0466:                }
0467:                if (minDigits < 0 || maxDigits <= 0) {
0468:                    throw new IllegalArgumentException();
0469:                }
0470:                if (minDigits <= 1) {
0471:                    return append0(new UnpaddedNumber(fieldType, maxDigits,
0472:                            true));
0473:                } else {
0474:                    return append0(new PaddedNumber(fieldType, maxDigits, true,
0475:                            minDigits));
0476:                }
0477:            }
0478:
0479:            /**
0480:             * Instructs the printer to emit a field value as a fixed-width decimal
0481:             * number (smaller numbers will be left-padded with zeros), and the parser
0482:             * to expect an signed decimal number with the same fixed width.
0483:             * 
0484:             * @param fieldType type of field to append
0485:             * @param numDigits the exact number of digits to parse or print, except if
0486:             * printed value requires more digits
0487:             * @return this DateTimeFormatterBuilder
0488:             * @throws IllegalArgumentException if field type is null or if <code>numDigits <= 0</code>
0489:             * @since 1.5
0490:             */
0491:            public DateTimeFormatterBuilder appendFixedSignedDecimal(
0492:                    DateTimeFieldType fieldType, int numDigits) {
0493:                if (fieldType == null) {
0494:                    throw new IllegalArgumentException(
0495:                            "Field type must not be null");
0496:                }
0497:                if (numDigits <= 0) {
0498:                    throw new IllegalArgumentException(
0499:                            "Illegal number of digits: " + numDigits);
0500:                }
0501:                return append0(new FixedNumber(fieldType, numDigits, true));
0502:            }
0503:
0504:            /**
0505:             * Instructs the printer to emit a field value as text, and the
0506:             * parser to expect text.
0507:             *
0508:             * @param fieldType type of field to append
0509:             * @return this DateTimeFormatterBuilder
0510:             * @throws IllegalArgumentException if field type is null
0511:             */
0512:            public DateTimeFormatterBuilder appendText(
0513:                    DateTimeFieldType fieldType) {
0514:                if (fieldType == null) {
0515:                    throw new IllegalArgumentException(
0516:                            "Field type must not be null");
0517:                }
0518:                return append0(new TextField(fieldType, false));
0519:            }
0520:
0521:            /**
0522:             * Instructs the printer to emit a field value as short text, and the
0523:             * parser to expect text.
0524:             *
0525:             * @param fieldType type of field to append
0526:             * @return this DateTimeFormatterBuilder
0527:             * @throws IllegalArgumentException if field type is null
0528:             */
0529:            public DateTimeFormatterBuilder appendShortText(
0530:                    DateTimeFieldType fieldType) {
0531:                if (fieldType == null) {
0532:                    throw new IllegalArgumentException(
0533:                            "Field type must not be null");
0534:                }
0535:                return append0(new TextField(fieldType, true));
0536:            }
0537:
0538:            /**
0539:             * Instructs the printer to emit a remainder of time as a decimal fraction,
0540:             * sans decimal point. For example, if the field is specified as
0541:             * minuteOfHour and the time is 12:30:45, the value printed is 75. A
0542:             * decimal point is implied, so the fraction is 0.75, or three-quarters of
0543:             * a minute.
0544:             *
0545:             * @param fieldType type of field to append
0546:             * @param minDigits minumum number of digits to print.
0547:             * @param maxDigits maximum number of digits to print or parse.
0548:             * @return this DateTimeFormatterBuilder
0549:             * @throws IllegalArgumentException if field type is null
0550:             */
0551:            public DateTimeFormatterBuilder appendFraction(
0552:                    DateTimeFieldType fieldType, int minDigits, int maxDigits) {
0553:                if (fieldType == null) {
0554:                    throw new IllegalArgumentException(
0555:                            "Field type must not be null");
0556:                }
0557:                if (maxDigits < minDigits) {
0558:                    maxDigits = minDigits;
0559:                }
0560:                if (minDigits < 0 || maxDigits <= 0) {
0561:                    throw new IllegalArgumentException();
0562:                }
0563:                return append0(new Fraction(fieldType, minDigits, maxDigits));
0564:            }
0565:
0566:            /**
0567:             * @param minDigits minumum number of digits to print
0568:             * @param maxDigits maximum number of digits to print or parse
0569:             * @return this DateTimeFormatterBuilder
0570:             */
0571:            public DateTimeFormatterBuilder appendFractionOfSecond(
0572:                    int minDigits, int maxDigits) {
0573:                return appendFraction(DateTimeFieldType.secondOfDay(),
0574:                        minDigits, maxDigits);
0575:            }
0576:
0577:            /**
0578:             * @param minDigits minumum number of digits to print
0579:             * @param maxDigits maximum number of digits to print or parse
0580:             * @return this DateTimeFormatterBuilder
0581:             */
0582:            public DateTimeFormatterBuilder appendFractionOfMinute(
0583:                    int minDigits, int maxDigits) {
0584:                return appendFraction(DateTimeFieldType.minuteOfDay(),
0585:                        minDigits, maxDigits);
0586:            }
0587:
0588:            /**
0589:             * @param minDigits minumum number of digits to print
0590:             * @param maxDigits maximum number of digits to print or parse
0591:             * @return this DateTimeFormatterBuilder
0592:             */
0593:            public DateTimeFormatterBuilder appendFractionOfHour(int minDigits,
0594:                    int maxDigits) {
0595:                return appendFraction(DateTimeFieldType.hourOfDay(), minDigits,
0596:                        maxDigits);
0597:            }
0598:
0599:            /**
0600:             * @param minDigits minumum number of digits to print
0601:             * @param maxDigits maximum number of digits to print or parse
0602:             * @return this DateTimeFormatterBuilder
0603:             */
0604:            public DateTimeFormatterBuilder appendFractionOfDay(int minDigits,
0605:                    int maxDigits) {
0606:                return appendFraction(DateTimeFieldType.dayOfYear(), minDigits,
0607:                        maxDigits);
0608:            }
0609:
0610:            /**
0611:             * Instructs the printer to emit a numeric millisOfSecond field.
0612:             *
0613:             * @param minDigits minumum number of digits to print
0614:             * @return this DateTimeFormatterBuilder
0615:             */
0616:            public DateTimeFormatterBuilder appendMillisOfSecond(int minDigits) {
0617:                return appendDecimal(DateTimeFieldType.millisOfSecond(),
0618:                        minDigits, 3);
0619:            }
0620:
0621:            /**
0622:             * Instructs the printer to emit a numeric millisOfDay field.
0623:             *
0624:             * @param minDigits minumum number of digits to print
0625:             * @return this DateTimeFormatterBuilder
0626:             */
0627:            public DateTimeFormatterBuilder appendMillisOfDay(int minDigits) {
0628:                return appendDecimal(DateTimeFieldType.millisOfDay(),
0629:                        minDigits, 8);
0630:            }
0631:
0632:            /**
0633:             * Instructs the printer to emit a numeric secondOfMinute field.
0634:             *
0635:             * @param minDigits minumum number of digits to print
0636:             * @return this DateTimeFormatterBuilder
0637:             */
0638:            public DateTimeFormatterBuilder appendSecondOfMinute(int minDigits) {
0639:                return appendDecimal(DateTimeFieldType.secondOfMinute(),
0640:                        minDigits, 2);
0641:            }
0642:
0643:            /**
0644:             * Instructs the printer to emit a numeric secondOfDay field.
0645:             *
0646:             * @param minDigits minumum number of digits to print
0647:             * @return this DateTimeFormatterBuilder
0648:             */
0649:            public DateTimeFormatterBuilder appendSecondOfDay(int minDigits) {
0650:                return appendDecimal(DateTimeFieldType.secondOfDay(),
0651:                        minDigits, 5);
0652:            }
0653:
0654:            /**
0655:             * Instructs the printer to emit a numeric minuteOfHour field.
0656:             *
0657:             * @param minDigits minumum number of digits to print
0658:             * @return this DateTimeFormatterBuilder
0659:             */
0660:            public DateTimeFormatterBuilder appendMinuteOfHour(int minDigits) {
0661:                return appendDecimal(DateTimeFieldType.minuteOfHour(),
0662:                        minDigits, 2);
0663:            }
0664:
0665:            /**
0666:             * Instructs the printer to emit a numeric minuteOfDay field.
0667:             *
0668:             * @param minDigits minumum number of digits to print
0669:             * @return this DateTimeFormatterBuilder
0670:             */
0671:            public DateTimeFormatterBuilder appendMinuteOfDay(int minDigits) {
0672:                return appendDecimal(DateTimeFieldType.minuteOfDay(),
0673:                        minDigits, 4);
0674:            }
0675:
0676:            /**
0677:             * Instructs the printer to emit a numeric hourOfDay field.
0678:             *
0679:             * @param minDigits minumum number of digits to print
0680:             * @return this DateTimeFormatterBuilder
0681:             */
0682:            public DateTimeFormatterBuilder appendHourOfDay(int minDigits) {
0683:                return appendDecimal(DateTimeFieldType.hourOfDay(), minDigits,
0684:                        2);
0685:            }
0686:
0687:            /**
0688:             * Instructs the printer to emit a numeric clockhourOfDay field.
0689:             *
0690:             * @param minDigits minumum number of digits to print
0691:             * @return this DateTimeFormatterBuilder
0692:             */
0693:            public DateTimeFormatterBuilder appendClockhourOfDay(int minDigits) {
0694:                return appendDecimal(DateTimeFieldType.clockhourOfDay(),
0695:                        minDigits, 2);
0696:            }
0697:
0698:            /**
0699:             * Instructs the printer to emit a numeric hourOfHalfday field.
0700:             *
0701:             * @param minDigits minumum number of digits to print
0702:             * @return this DateTimeFormatterBuilder
0703:             */
0704:            public DateTimeFormatterBuilder appendHourOfHalfday(int minDigits) {
0705:                return appendDecimal(DateTimeFieldType.hourOfHalfday(),
0706:                        minDigits, 2);
0707:            }
0708:
0709:            /**
0710:             * Instructs the printer to emit a numeric clockhourOfHalfday field.
0711:             *
0712:             * @param minDigits minumum number of digits to print
0713:             * @return this DateTimeFormatterBuilder
0714:             */
0715:            public DateTimeFormatterBuilder appendClockhourOfHalfday(
0716:                    int minDigits) {
0717:                return appendDecimal(DateTimeFieldType.clockhourOfHalfday(),
0718:                        minDigits, 2);
0719:            }
0720:
0721:            /**
0722:             * Instructs the printer to emit a numeric dayOfWeek field.
0723:             *
0724:             * @param minDigits minumum number of digits to print
0725:             * @return this DateTimeFormatterBuilder
0726:             */
0727:            public DateTimeFormatterBuilder appendDayOfWeek(int minDigits) {
0728:                return appendDecimal(DateTimeFieldType.dayOfWeek(), minDigits,
0729:                        1);
0730:            }
0731:
0732:            /**
0733:             * Instructs the printer to emit a numeric dayOfMonth field.
0734:             *
0735:             * @param minDigits minumum number of digits to print
0736:             * @return this DateTimeFormatterBuilder
0737:             */
0738:            public DateTimeFormatterBuilder appendDayOfMonth(int minDigits) {
0739:                return appendDecimal(DateTimeFieldType.dayOfMonth(), minDigits,
0740:                        2);
0741:            }
0742:
0743:            /**
0744:             * Instructs the printer to emit a numeric dayOfYear field.
0745:             *
0746:             * @param minDigits minumum number of digits to print
0747:             * @return this DateTimeFormatterBuilder
0748:             */
0749:            public DateTimeFormatterBuilder appendDayOfYear(int minDigits) {
0750:                return appendDecimal(DateTimeFieldType.dayOfYear(), minDigits,
0751:                        3);
0752:            }
0753:
0754:            /**
0755:             * Instructs the printer to emit a numeric weekOfWeekyear field.
0756:             *
0757:             * @param minDigits minumum number of digits to print
0758:             * @return this DateTimeFormatterBuilder
0759:             */
0760:            public DateTimeFormatterBuilder appendWeekOfWeekyear(int minDigits) {
0761:                return appendDecimal(DateTimeFieldType.weekOfWeekyear(),
0762:                        minDigits, 2);
0763:            }
0764:
0765:            /**
0766:             * Instructs the printer to emit a numeric weekyear field.
0767:             *
0768:             * @param minDigits minumum number of digits to <i>print</i>
0769:             * @param maxDigits maximum number of digits to <i>parse</i>, or the estimated
0770:             * maximum number of digits to print
0771:             * @return this DateTimeFormatterBuilder
0772:             */
0773:            public DateTimeFormatterBuilder appendWeekyear(int minDigits,
0774:                    int maxDigits) {
0775:                return appendSignedDecimal(DateTimeFieldType.weekyear(),
0776:                        minDigits, maxDigits);
0777:            }
0778:
0779:            /**
0780:             * Instructs the printer to emit a numeric monthOfYear field.
0781:             *
0782:             * @param minDigits minumum number of digits to print
0783:             * @return this DateTimeFormatterBuilder
0784:             */
0785:            public DateTimeFormatterBuilder appendMonthOfYear(int minDigits) {
0786:                return appendDecimal(DateTimeFieldType.monthOfYear(),
0787:                        minDigits, 2);
0788:            }
0789:
0790:            /**
0791:             * Instructs the printer to emit a numeric year field.
0792:             *
0793:             * @param minDigits minumum number of digits to <i>print</i>
0794:             * @param maxDigits maximum number of digits to <i>parse</i>, or the estimated
0795:             * maximum number of digits to print
0796:             * @return this DateTimeFormatterBuilder
0797:             */
0798:            public DateTimeFormatterBuilder appendYear(int minDigits,
0799:                    int maxDigits) {
0800:                return appendSignedDecimal(DateTimeFieldType.year(), minDigits,
0801:                        maxDigits);
0802:            }
0803:
0804:            /**
0805:             * Instructs the printer to emit a numeric year field which always prints
0806:             * and parses two digits. A pivot year is used during parsing to determine
0807:             * the range of supported years as <code>(pivot - 50) .. (pivot + 49)</code>.
0808:             *
0809:             * <pre>
0810:             * pivot   supported range   00 is   20 is   40 is   60 is   80 is
0811:             * ---------------------------------------------------------------
0812:             * 1950      1900..1999      1900    1920    1940    1960    1980
0813:             * 1975      1925..2024      2000    2020    1940    1960    1980
0814:             * 2000      1950..2049      2000    2020    2040    1960    1980
0815:             * 2025      1975..2074      2000    2020    2040    2060    1980
0816:             * 2050      2000..2099      2000    2020    2040    2060    2080
0817:             * </pre>
0818:             *
0819:             * @param pivot  pivot year to use when parsing
0820:             * @return this DateTimeFormatterBuilder
0821:             */
0822:            public DateTimeFormatterBuilder appendTwoDigitYear(int pivot) {
0823:                return appendTwoDigitYear(pivot, false);
0824:            }
0825:
0826:            /**
0827:             * Instructs the printer to emit a numeric year field which always prints
0828:             * two digits. A pivot year is used during parsing to determine the range
0829:             * of supported years as <code>(pivot - 50) .. (pivot + 49)</code>. If
0830:             * parse is instructed to be lenient and the digit count is not two, it is
0831:             * treated as an absolute year. With lenient parsing, specifying a positive
0832:             * or negative sign before the year also makes it absolute.
0833:             *
0834:             * @param pivot  pivot year to use when parsing
0835:             * @param lenientParse  when true, if digit count is not two, it is treated
0836:             * as an absolute year
0837:             * @return this DateTimeFormatterBuilder
0838:             * @since 1.1
0839:             */
0840:            public DateTimeFormatterBuilder appendTwoDigitYear(int pivot,
0841:                    boolean lenientParse) {
0842:                return append0(new TwoDigitYear(DateTimeFieldType.year(),
0843:                        pivot, lenientParse));
0844:            }
0845:
0846:            /**
0847:             * Instructs the printer to emit a numeric weekyear field which always prints
0848:             * and parses two digits. A pivot year is used during parsing to determine
0849:             * the range of supported years as <code>(pivot - 50) .. (pivot + 49)</code>.
0850:             *
0851:             * <pre>
0852:             * pivot   supported range   00 is   20 is   40 is   60 is   80 is
0853:             * ---------------------------------------------------------------
0854:             * 1950      1900..1999      1900    1920    1940    1960    1980
0855:             * 1975      1925..2024      2000    2020    1940    1960    1980
0856:             * 2000      1950..2049      2000    2020    2040    1960    1980
0857:             * 2025      1975..2074      2000    2020    2040    2060    1980
0858:             * 2050      2000..2099      2000    2020    2040    2060    2080
0859:             * </pre>
0860:             *
0861:             * @param pivot  pivot weekyear to use when parsing
0862:             * @return this DateTimeFormatterBuilder
0863:             */
0864:            public DateTimeFormatterBuilder appendTwoDigitWeekyear(int pivot) {
0865:                return appendTwoDigitWeekyear(pivot, false);
0866:            }
0867:
0868:            /**
0869:             * Instructs the printer to emit a numeric weekyear field which always prints
0870:             * two digits. A pivot year is used during parsing to determine the range
0871:             * of supported years as <code>(pivot - 50) .. (pivot + 49)</code>. If
0872:             * parse is instructed to be lenient and the digit count is not two, it is
0873:             * treated as an absolute weekyear. With lenient parsing, specifying a positive
0874:             * or negative sign before the weekyear also makes it absolute.
0875:             *
0876:             * @param pivot  pivot weekyear to use when parsing
0877:             * @param lenientParse  when true, if digit count is not two, it is treated
0878:             * as an absolute weekyear
0879:             * @return this DateTimeFormatterBuilder
0880:             * @since 1.1
0881:             */
0882:            public DateTimeFormatterBuilder appendTwoDigitWeekyear(int pivot,
0883:                    boolean lenientParse) {
0884:                return append0(new TwoDigitYear(DateTimeFieldType.weekyear(),
0885:                        pivot, lenientParse));
0886:            }
0887:
0888:            /**
0889:             * Instructs the printer to emit a numeric yearOfEra field.
0890:             *
0891:             * @param minDigits minumum number of digits to <i>print</i>
0892:             * @param maxDigits maximum number of digits to <i>parse</i>, or the estimated
0893:             * maximum number of digits to print
0894:             * @return this DateTimeFormatterBuilder
0895:             */
0896:            public DateTimeFormatterBuilder appendYearOfEra(int minDigits,
0897:                    int maxDigits) {
0898:                return appendDecimal(DateTimeFieldType.yearOfEra(), minDigits,
0899:                        maxDigits);
0900:            }
0901:
0902:            /**
0903:             * Instructs the printer to emit a numeric year of century field.
0904:             *
0905:             * @param minDigits minumum number of digits to print
0906:             * @param maxDigits maximum number of digits to <i>parse</i>, or the estimated
0907:             * maximum number of digits to print
0908:             * @return this DateTimeFormatterBuilder
0909:             */
0910:            public DateTimeFormatterBuilder appendYearOfCentury(int minDigits,
0911:                    int maxDigits) {
0912:                return appendDecimal(DateTimeFieldType.yearOfCentury(),
0913:                        minDigits, maxDigits);
0914:            }
0915:
0916:            /**
0917:             * Instructs the printer to emit a numeric century of era field.
0918:             *
0919:             * @param minDigits minumum number of digits to print
0920:             * @param maxDigits maximum number of digits to <i>parse</i>, or the estimated
0921:             * maximum number of digits to print
0922:             * @return this DateTimeFormatterBuilder
0923:             */
0924:            public DateTimeFormatterBuilder appendCenturyOfEra(int minDigits,
0925:                    int maxDigits) {
0926:                return appendSignedDecimal(DateTimeFieldType.centuryOfEra(),
0927:                        minDigits, maxDigits);
0928:            }
0929:
0930:            /**
0931:             * Instructs the printer to emit a locale-specific AM/PM text, and the
0932:             * parser to expect it. The parser is case-insensitive.
0933:             *
0934:             * @return this DateTimeFormatterBuilder
0935:             */
0936:            public DateTimeFormatterBuilder appendHalfdayOfDayText() {
0937:                return appendText(DateTimeFieldType.halfdayOfDay());
0938:            }
0939:
0940:            /**
0941:             * Instructs the printer to emit a locale-specific dayOfWeek text. The
0942:             * parser will accept a long or short dayOfWeek text, case-insensitive.
0943:             *
0944:             * @return this DateTimeFormatterBuilder
0945:             */
0946:            public DateTimeFormatterBuilder appendDayOfWeekText() {
0947:                return appendText(DateTimeFieldType.dayOfWeek());
0948:            }
0949:
0950:            /**
0951:             * Instructs the printer to emit a short locale-specific dayOfWeek
0952:             * text. The parser will accept a long or short dayOfWeek text,
0953:             * case-insensitive.
0954:             *
0955:             * @return this DateTimeFormatterBuilder
0956:             */
0957:            public DateTimeFormatterBuilder appendDayOfWeekShortText() {
0958:                return appendShortText(DateTimeFieldType.dayOfWeek());
0959:            }
0960:
0961:            /**
0962:             * Instructs the printer to emit a short locale-specific monthOfYear
0963:             * text. The parser will accept a long or short monthOfYear text,
0964:             * case-insensitive.
0965:             *
0966:             * @return this DateTimeFormatterBuilder
0967:             */
0968:            public DateTimeFormatterBuilder appendMonthOfYearText() {
0969:                return appendText(DateTimeFieldType.monthOfYear());
0970:            }
0971:
0972:            /**
0973:             * Instructs the printer to emit a locale-specific monthOfYear text. The
0974:             * parser will accept a long or short monthOfYear text, case-insensitive.
0975:             *
0976:             * @return this DateTimeFormatterBuilder
0977:             */
0978:            public DateTimeFormatterBuilder appendMonthOfYearShortText() {
0979:                return appendShortText(DateTimeFieldType.monthOfYear());
0980:            }
0981:
0982:            /**
0983:             * Instructs the printer to emit a locale-specific era text (BC/AD), and
0984:             * the parser to expect it. The parser is case-insensitive.
0985:             *
0986:             * @return this DateTimeFormatterBuilder
0987:             */
0988:            public DateTimeFormatterBuilder appendEraText() {
0989:                return appendText(DateTimeFieldType.era());
0990:            }
0991:
0992:            /**
0993:             * Instructs the printer to emit a locale-specific time zone name. A
0994:             * parser cannot be created from this builder if a time zone name is
0995:             * appended.
0996:             *
0997:             * @return this DateTimeFormatterBuilder
0998:             */
0999:            public DateTimeFormatterBuilder appendTimeZoneName() {
1000:                return append0(new TimeZoneName(TimeZoneName.LONG_NAME), null);
1001:            }
1002:
1003:            /**
1004:             * Instructs the printer to emit a short locale-specific time zone
1005:             * name. A parser cannot be created from this builder if time zone
1006:             * name is appended.
1007:             *
1008:             * @return this DateTimeFormatterBuilder
1009:             */
1010:            public DateTimeFormatterBuilder appendTimeZoneShortName() {
1011:                return append0(new TimeZoneName(TimeZoneName.SHORT_NAME), null);
1012:            }
1013:
1014:            /**
1015:             * Instructs the printer to emit the identifier of the time zone.
1016:             * This field cannot currently be parsed.
1017:             *
1018:             * @return this DateTimeFormatterBuilder
1019:             */
1020:            public DateTimeFormatterBuilder appendTimeZoneId() {
1021:                return append0(new TimeZoneName(TimeZoneName.ID), null);
1022:            }
1023:
1024:            /**
1025:             * Instructs the printer to emit text and numbers to display time zone
1026:             * offset from UTC. A parser will use the parsed time zone offset to adjust
1027:             * the datetime.
1028:             *
1029:             * @param zeroOffsetText Text to use if time zone offset is zero. If
1030:             * null, offset is always shown.
1031:             * @param showSeparators If true, prints ':' separator before minute and
1032:             * second field and prints '.' separator before fraction field.
1033:             * @param minFields minimum number of fields to print, stopping when no
1034:             * more precision is required. 1=hours, 2=minutes, 3=seconds, 4=fraction
1035:             * @param maxFields maximum number of fields to print
1036:             * @return this DateTimeFormatterBuilder
1037:             */
1038:            public DateTimeFormatterBuilder appendTimeZoneOffset(
1039:                    String zeroOffsetText, boolean showSeparators,
1040:                    int minFields, int maxFields) {
1041:                return append0(new TimeZoneOffset(zeroOffsetText,
1042:                        showSeparators, minFields, maxFields));
1043:            }
1044:
1045:            //-----------------------------------------------------------------------
1046:            /**
1047:             * Calls upon {@link DateTimeFormat} to parse the pattern and append the
1048:             * results into this builder.
1049:             *
1050:             * @param pattern  pattern specification
1051:             * @throws IllegalArgumentException if the pattern is invalid
1052:             * @see DateTimeFormat
1053:             */
1054:            public DateTimeFormatterBuilder appendPattern(String pattern) {
1055:                DateTimeFormat.appendPatternTo(this , pattern);
1056:                return this ;
1057:            }
1058:
1059:            //-----------------------------------------------------------------------
1060:            private Object getFormatter() {
1061:                Object f = iFormatter;
1062:
1063:                if (f == null) {
1064:                    if (iElementPairs.size() == 2) {
1065:                        Object printer = iElementPairs.get(0);
1066:                        Object parser = iElementPairs.get(1);
1067:
1068:                        if (printer != null) {
1069:                            if (printer == parser || parser == null) {
1070:                                f = printer;
1071:                            }
1072:                        } else {
1073:                            f = parser;
1074:                        }
1075:                    }
1076:
1077:                    if (f == null) {
1078:                        f = new Composite(iElementPairs);
1079:                    }
1080:
1081:                    iFormatter = f;
1082:                }
1083:
1084:                return f;
1085:            }
1086:
1087:            private boolean isPrinter(Object f) {
1088:                if (f instanceof  DateTimePrinter) {
1089:                    if (f instanceof  Composite) {
1090:                        return ((Composite) f).isPrinter();
1091:                    }
1092:                    return true;
1093:                }
1094:                return false;
1095:            }
1096:
1097:            private boolean isParser(Object f) {
1098:                if (f instanceof  DateTimeParser) {
1099:                    if (f instanceof  Composite) {
1100:                        return ((Composite) f).isParser();
1101:                    }
1102:                    return true;
1103:                }
1104:                return false;
1105:            }
1106:
1107:            private boolean isFormatter(Object f) {
1108:                return (isPrinter(f) || isParser(f));
1109:            }
1110:
1111:            static void appendUnknownString(StringBuffer buf, int len) {
1112:                for (int i = len; --i >= 0;) {
1113:                    buf.append('\ufffd');
1114:                }
1115:            }
1116:
1117:            static void printUnknownString(Writer out, int len)
1118:                    throws IOException {
1119:                for (int i = len; --i >= 0;) {
1120:                    out.write('\ufffd');
1121:                }
1122:            }
1123:
1124:            //-----------------------------------------------------------------------
1125:            static class CharacterLiteral implements  DateTimePrinter,
1126:                    DateTimeParser {
1127:
1128:                private final char iValue;
1129:
1130:                CharacterLiteral(char value) {
1131:                    super ();
1132:                    iValue = value;
1133:                }
1134:
1135:                public int estimatePrintedLength() {
1136:                    return 1;
1137:                }
1138:
1139:                public void printTo(StringBuffer buf, long instant,
1140:                        Chronology chrono, int displayOffset,
1141:                        DateTimeZone displayZone, Locale locale) {
1142:                    buf.append(iValue);
1143:                }
1144:
1145:                public void printTo(Writer out, long instant,
1146:                        Chronology chrono, int displayOffset,
1147:                        DateTimeZone displayZone, Locale locale)
1148:                        throws IOException {
1149:                    out.write(iValue);
1150:                }
1151:
1152:                public void printTo(StringBuffer buf, ReadablePartial partial,
1153:                        Locale locale) {
1154:                    buf.append(iValue);
1155:                }
1156:
1157:                public void printTo(Writer out, ReadablePartial partial,
1158:                        Locale locale) throws IOException {
1159:                    out.write(iValue);
1160:                }
1161:
1162:                public int estimateParsedLength() {
1163:                    return 1;
1164:                }
1165:
1166:                public int parseInto(DateTimeParserBucket bucket, String text,
1167:                        int position) {
1168:                    if (position >= text.length()) {
1169:                        return ~position;
1170:                    }
1171:
1172:                    char a = text.charAt(position);
1173:                    char b = iValue;
1174:
1175:                    if (a != b) {
1176:                        a = Character.toUpperCase(a);
1177:                        b = Character.toUpperCase(b);
1178:                        if (a != b) {
1179:                            a = Character.toLowerCase(a);
1180:                            b = Character.toLowerCase(b);
1181:                            if (a != b) {
1182:                                return ~position;
1183:                            }
1184:                        }
1185:                    }
1186:
1187:                    return position + 1;
1188:                }
1189:            }
1190:
1191:            //-----------------------------------------------------------------------
1192:            static class StringLiteral implements  DateTimePrinter,
1193:                    DateTimeParser {
1194:
1195:                private final String iValue;
1196:
1197:                StringLiteral(String value) {
1198:                    super ();
1199:                    iValue = value;
1200:                }
1201:
1202:                public int estimatePrintedLength() {
1203:                    return iValue.length();
1204:                }
1205:
1206:                public void printTo(StringBuffer buf, long instant,
1207:                        Chronology chrono, int displayOffset,
1208:                        DateTimeZone displayZone, Locale locale) {
1209:                    buf.append(iValue);
1210:                }
1211:
1212:                public void printTo(Writer out, long instant,
1213:                        Chronology chrono, int displayOffset,
1214:                        DateTimeZone displayZone, Locale locale)
1215:                        throws IOException {
1216:                    out.write(iValue);
1217:                }
1218:
1219:                public void printTo(StringBuffer buf, ReadablePartial partial,
1220:                        Locale locale) {
1221:                    buf.append(iValue);
1222:                }
1223:
1224:                public void printTo(Writer out, ReadablePartial partial,
1225:                        Locale locale) throws IOException {
1226:                    out.write(iValue);
1227:                }
1228:
1229:                public int estimateParsedLength() {
1230:                    return iValue.length();
1231:                }
1232:
1233:                public int parseInto(DateTimeParserBucket bucket, String text,
1234:                        int position) {
1235:                    if (text.regionMatches(true, position, iValue, 0, iValue
1236:                            .length())) {
1237:                        return position + iValue.length();
1238:                    }
1239:                    return ~position;
1240:                }
1241:            }
1242:
1243:            //-----------------------------------------------------------------------
1244:            static abstract class NumberFormatter implements  DateTimePrinter,
1245:                    DateTimeParser {
1246:                protected final DateTimeFieldType iFieldType;
1247:                protected final int iMaxParsedDigits;
1248:                protected final boolean iSigned;
1249:
1250:                NumberFormatter(DateTimeFieldType fieldType,
1251:                        int maxParsedDigits, boolean signed) {
1252:                    super ();
1253:                    iFieldType = fieldType;
1254:                    iMaxParsedDigits = maxParsedDigits;
1255:                    iSigned = signed;
1256:                }
1257:
1258:                public int estimateParsedLength() {
1259:                    return iMaxParsedDigits;
1260:                }
1261:
1262:                public int parseInto(DateTimeParserBucket bucket, String text,
1263:                        int position) {
1264:                    int limit = Math.min(iMaxParsedDigits, text.length()
1265:                            - position);
1266:
1267:                    boolean negative = false;
1268:                    int length = 0;
1269:                    while (length < limit) {
1270:                        char c = text.charAt(position + length);
1271:                        if (length == 0 && (c == '-' || c == '+') && iSigned) {
1272:                            negative = c == '-';
1273:
1274:                            // Next character must be a digit.
1275:                            if (length + 1 >= limit
1276:                                    || (c = text.charAt(position + length + 1)) < '0'
1277:                                    || c > '9') {
1278:                                break;
1279:                            }
1280:
1281:                            if (negative) {
1282:                                length++;
1283:                            } else {
1284:                                // Skip the '+' for parseInt to succeed.
1285:                                position++;
1286:                            }
1287:                            // Expand the limit to disregard the sign character.
1288:                            limit = Math.min(limit + 1, text.length()
1289:                                    - position);
1290:                            continue;
1291:                        }
1292:                        if (c < '0' || c > '9') {
1293:                            break;
1294:                        }
1295:                        length++;
1296:                    }
1297:
1298:                    if (length == 0) {
1299:                        return ~position;
1300:                    }
1301:
1302:                    int value;
1303:                    if (length >= 9) {
1304:                        // Since value may exceed integer limits, use stock parser
1305:                        // which checks for this.
1306:                        value = Integer.parseInt(text.substring(position,
1307:                                position += length));
1308:                    } else {
1309:                        int i = position;
1310:                        if (negative) {
1311:                            i++;
1312:                        }
1313:                        try {
1314:                            value = text.charAt(i++) - '0';
1315:                        } catch (StringIndexOutOfBoundsException e) {
1316:                            return ~position;
1317:                        }
1318:                        position += length;
1319:                        while (i < position) {
1320:                            value = ((value << 3) + (value << 1))
1321:                                    + text.charAt(i++) - '0';
1322:                        }
1323:                        if (negative) {
1324:                            value = -value;
1325:                        }
1326:                    }
1327:
1328:                    bucket.saveField(iFieldType, value);
1329:                    return position;
1330:                }
1331:            }
1332:
1333:            //-----------------------------------------------------------------------
1334:            static class UnpaddedNumber extends NumberFormatter {
1335:
1336:                protected UnpaddedNumber(DateTimeFieldType fieldType,
1337:                        int maxParsedDigits, boolean signed) {
1338:                    super (fieldType, maxParsedDigits, signed);
1339:                }
1340:
1341:                public int estimatePrintedLength() {
1342:                    return iMaxParsedDigits;
1343:                }
1344:
1345:                public void printTo(StringBuffer buf, long instant,
1346:                        Chronology chrono, int displayOffset,
1347:                        DateTimeZone displayZone, Locale locale) {
1348:                    try {
1349:                        DateTimeField field = iFieldType.getField(chrono);
1350:                        FormatUtils.appendUnpaddedInteger(buf, field
1351:                                .get(instant));
1352:                    } catch (RuntimeException e) {
1353:                        buf.append('\ufffd');
1354:                    }
1355:                }
1356:
1357:                public void printTo(Writer out, long instant,
1358:                        Chronology chrono, int displayOffset,
1359:                        DateTimeZone displayZone, Locale locale)
1360:                        throws IOException {
1361:                    try {
1362:                        DateTimeField field = iFieldType.getField(chrono);
1363:                        FormatUtils.writeUnpaddedInteger(out, field
1364:                                .get(instant));
1365:                    } catch (RuntimeException e) {
1366:                        out.write('\ufffd');
1367:                    }
1368:                }
1369:
1370:                public void printTo(StringBuffer buf, ReadablePartial partial,
1371:                        Locale locale) {
1372:                    if (partial.isSupported(iFieldType)) {
1373:                        try {
1374:                            FormatUtils.appendUnpaddedInteger(buf, partial
1375:                                    .get(iFieldType));
1376:                        } catch (RuntimeException e) {
1377:                            buf.append('\ufffd');
1378:                        }
1379:                    } else {
1380:                        buf.append('\ufffd');
1381:                    }
1382:                }
1383:
1384:                public void printTo(Writer out, ReadablePartial partial,
1385:                        Locale locale) throws IOException {
1386:                    if (partial.isSupported(iFieldType)) {
1387:                        try {
1388:                            FormatUtils.writeUnpaddedInteger(out, partial
1389:                                    .get(iFieldType));
1390:                        } catch (RuntimeException e) {
1391:                            out.write('\ufffd');
1392:                        }
1393:                    } else {
1394:                        out.write('\ufffd');
1395:                    }
1396:                }
1397:            }
1398:
1399:            //-----------------------------------------------------------------------
1400:            static class PaddedNumber extends NumberFormatter {
1401:
1402:                protected final int iMinPrintedDigits;
1403:
1404:                protected PaddedNumber(DateTimeFieldType fieldType,
1405:                        int maxParsedDigits, boolean signed,
1406:                        int minPrintedDigits) {
1407:                    super (fieldType, maxParsedDigits, signed);
1408:                    iMinPrintedDigits = minPrintedDigits;
1409:                }
1410:
1411:                public int estimatePrintedLength() {
1412:                    return iMaxParsedDigits;
1413:                }
1414:
1415:                public void printTo(StringBuffer buf, long instant,
1416:                        Chronology chrono, int displayOffset,
1417:                        DateTimeZone displayZone, Locale locale) {
1418:                    try {
1419:                        DateTimeField field = iFieldType.getField(chrono);
1420:                        FormatUtils.appendPaddedInteger(buf,
1421:                                field.get(instant), iMinPrintedDigits);
1422:                    } catch (RuntimeException e) {
1423:                        appendUnknownString(buf, iMinPrintedDigits);
1424:                    }
1425:                }
1426:
1427:                public void printTo(Writer out, long instant,
1428:                        Chronology chrono, int displayOffset,
1429:                        DateTimeZone displayZone, Locale locale)
1430:                        throws IOException {
1431:                    try {
1432:                        DateTimeField field = iFieldType.getField(chrono);
1433:                        FormatUtils.writePaddedInteger(out, field.get(instant),
1434:                                iMinPrintedDigits);
1435:                    } catch (RuntimeException e) {
1436:                        printUnknownString(out, iMinPrintedDigits);
1437:                    }
1438:                }
1439:
1440:                public void printTo(StringBuffer buf, ReadablePartial partial,
1441:                        Locale locale) {
1442:                    if (partial.isSupported(iFieldType)) {
1443:                        try {
1444:                            FormatUtils.appendPaddedInteger(buf, partial
1445:                                    .get(iFieldType), iMinPrintedDigits);
1446:                        } catch (RuntimeException e) {
1447:                            appendUnknownString(buf, iMinPrintedDigits);
1448:                        }
1449:                    } else {
1450:                        appendUnknownString(buf, iMinPrintedDigits);
1451:                    }
1452:                }
1453:
1454:                public void printTo(Writer out, ReadablePartial partial,
1455:                        Locale locale) throws IOException {
1456:                    if (partial.isSupported(iFieldType)) {
1457:                        try {
1458:                            FormatUtils.writePaddedInteger(out, partial
1459:                                    .get(iFieldType), iMinPrintedDigits);
1460:                        } catch (RuntimeException e) {
1461:                            printUnknownString(out, iMinPrintedDigits);
1462:                        }
1463:                    } else {
1464:                        printUnknownString(out, iMinPrintedDigits);
1465:                    }
1466:                }
1467:            }
1468:
1469:            //-----------------------------------------------------------------------
1470:            static class FixedNumber extends PaddedNumber {
1471:
1472:                protected FixedNumber(DateTimeFieldType fieldType,
1473:                        int numDigits, boolean signed) {
1474:                    super (fieldType, numDigits, signed, numDigits);
1475:                }
1476:
1477:                public int parseInto(DateTimeParserBucket bucket, String text,
1478:                        int position) {
1479:                    int newPos = super .parseInto(bucket, text, position);
1480:                    if (newPos < 0) {
1481:                        return newPos;
1482:                    }
1483:                    int expectedPos = position + iMaxParsedDigits;
1484:                    if (newPos != expectedPos) {
1485:                        if (iSigned) {
1486:                            char c = text.charAt(position);
1487:                            if (c == '-' || c == '+') {
1488:                                expectedPos++;
1489:                            }
1490:                        }
1491:                        if (newPos > expectedPos) {
1492:                            // The failure is at the position of the first extra digit.
1493:                            return ~(expectedPos + 1);
1494:                        } else if (newPos < expectedPos) {
1495:                            // The failure is at the position where the next digit should be.
1496:                            return ~newPos;
1497:                        }
1498:                    }
1499:                    return newPos;
1500:                }
1501:            }
1502:
1503:            //-----------------------------------------------------------------------
1504:            static class TwoDigitYear implements  DateTimePrinter,
1505:                    DateTimeParser {
1506:
1507:                /** The field to print/parse. */
1508:                private final DateTimeFieldType iType;
1509:                /** The pivot year. */
1510:                private final int iPivot;
1511:                private final boolean iLenientParse;
1512:
1513:                TwoDigitYear(DateTimeFieldType type, int pivot,
1514:                        boolean lenientParse) {
1515:                    super ();
1516:                    iType = type;
1517:                    iPivot = pivot;
1518:                    iLenientParse = lenientParse;
1519:                }
1520:
1521:                public int estimateParsedLength() {
1522:                    return iLenientParse ? 4 : 2;
1523:                }
1524:
1525:                public int parseInto(DateTimeParserBucket bucket, String text,
1526:                        int position) {
1527:                    int limit = text.length() - position;
1528:
1529:                    if (!iLenientParse) {
1530:                        limit = Math.min(2, limit);
1531:                        if (limit < 2) {
1532:                            return ~position;
1533:                        }
1534:                    } else {
1535:                        boolean hasSignChar = false;
1536:                        boolean negative = false;
1537:                        int length = 0;
1538:                        while (length < limit) {
1539:                            char c = text.charAt(position + length);
1540:                            if (length == 0 && (c == '-' || c == '+')) {
1541:                                hasSignChar = true;
1542:                                negative = c == '-';
1543:                                if (negative) {
1544:                                    length++;
1545:                                } else {
1546:                                    // Skip the '+' for parseInt to succeed.
1547:                                    position++;
1548:                                    limit--;
1549:                                }
1550:                                continue;
1551:                            }
1552:                            if (c < '0' || c > '9') {
1553:                                break;
1554:                            }
1555:                            length++;
1556:                        }
1557:
1558:                        if (length == 0) {
1559:                            return ~position;
1560:                        }
1561:
1562:                        if (hasSignChar || length != 2) {
1563:                            int value;
1564:                            if (length >= 9) {
1565:                                // Since value may exceed integer limits, use stock
1566:                                // parser which checks for this.
1567:                                value = Integer.parseInt(text.substring(
1568:                                        position, position += length));
1569:                            } else {
1570:                                int i = position;
1571:                                if (negative) {
1572:                                    i++;
1573:                                }
1574:                                try {
1575:                                    value = text.charAt(i++) - '0';
1576:                                } catch (StringIndexOutOfBoundsException e) {
1577:                                    return ~position;
1578:                                }
1579:                                position += length;
1580:                                while (i < position) {
1581:                                    value = ((value << 3) + (value << 1))
1582:                                            + text.charAt(i++) - '0';
1583:                                }
1584:                                if (negative) {
1585:                                    value = -value;
1586:                                }
1587:                            }
1588:
1589:                            bucket.saveField(iType, value);
1590:                            return position;
1591:                        }
1592:                    }
1593:
1594:                    int year;
1595:                    char c = text.charAt(position);
1596:                    if (c < '0' || c > '9') {
1597:                        return ~position;
1598:                    }
1599:                    year = c - '0';
1600:                    c = text.charAt(position + 1);
1601:                    if (c < '0' || c > '9') {
1602:                        return ~position;
1603:                    }
1604:                    year = ((year << 3) + (year << 1)) + c - '0';
1605:
1606:                    int pivot = iPivot;
1607:                    // If the bucket pivot year is non-null, use that when parsing
1608:                    if (bucket.getPivotYear() != null) {
1609:                        pivot = bucket.getPivotYear().intValue();
1610:                    }
1611:
1612:                    int low = pivot - 50;
1613:
1614:                    int t;
1615:                    if (low >= 0) {
1616:                        t = low % 100;
1617:                    } else {
1618:                        t = 99 + ((low + 1) % 100);
1619:                    }
1620:
1621:                    year += low + ((year < t) ? 100 : 0) - t;
1622:
1623:                    bucket.saveField(iType, year);
1624:                    return position + 2;
1625:                }
1626:
1627:                public int estimatePrintedLength() {
1628:                    return 2;
1629:                }
1630:
1631:                public void printTo(StringBuffer buf, long instant,
1632:                        Chronology chrono, int displayOffset,
1633:                        DateTimeZone displayZone, Locale locale) {
1634:                    int year = getTwoDigitYear(instant, chrono);
1635:                    if (year < 0) {
1636:                        buf.append('\ufffd');
1637:                        buf.append('\ufffd');
1638:                    } else {
1639:                        FormatUtils.appendPaddedInteger(buf, year, 2);
1640:                    }
1641:                }
1642:
1643:                public void printTo(Writer out, long instant,
1644:                        Chronology chrono, int displayOffset,
1645:                        DateTimeZone displayZone, Locale locale)
1646:                        throws IOException {
1647:                    int year = getTwoDigitYear(instant, chrono);
1648:                    if (year < 0) {
1649:                        out.write('\ufffd');
1650:                        out.write('\ufffd');
1651:                    } else {
1652:                        FormatUtils.writePaddedInteger(out, year, 2);
1653:                    }
1654:                }
1655:
1656:                private int getTwoDigitYear(long instant, Chronology chrono) {
1657:                    try {
1658:                        int year = iType.getField(chrono).get(instant);
1659:                        if (year < 0) {
1660:                            year = -year;
1661:                        }
1662:                        return year % 100;
1663:                    } catch (RuntimeException e) {
1664:                        return -1;
1665:                    }
1666:                }
1667:
1668:                public void printTo(StringBuffer buf, ReadablePartial partial,
1669:                        Locale locale) {
1670:                    int year = getTwoDigitYear(partial);
1671:                    if (year < 0) {
1672:                        buf.append('\ufffd');
1673:                        buf.append('\ufffd');
1674:                    } else {
1675:                        FormatUtils.appendPaddedInteger(buf, year, 2);
1676:                    }
1677:                }
1678:
1679:                public void printTo(Writer out, ReadablePartial partial,
1680:                        Locale locale) throws IOException {
1681:                    int year = getTwoDigitYear(partial);
1682:                    if (year < 0) {
1683:                        out.write('\ufffd');
1684:                        out.write('\ufffd');
1685:                    } else {
1686:                        FormatUtils.writePaddedInteger(out, year, 2);
1687:                    }
1688:                }
1689:
1690:                private int getTwoDigitYear(ReadablePartial partial) {
1691:                    if (partial.isSupported(iType)) {
1692:                        try {
1693:                            int year = partial.get(iType);
1694:                            if (year < 0) {
1695:                                year = -year;
1696:                            }
1697:                            return year % 100;
1698:                        } catch (RuntimeException e) {
1699:                        }
1700:                    }
1701:                    return -1;
1702:                }
1703:            }
1704:
1705:            //-----------------------------------------------------------------------
1706:            static class TextField implements  DateTimePrinter, DateTimeParser {
1707:
1708:                private static Map cParseCache = new HashMap();
1709:                private final DateTimeFieldType iFieldType;
1710:                private final boolean iShort;
1711:
1712:                TextField(DateTimeFieldType fieldType, boolean isShort) {
1713:                    super ();
1714:                    iFieldType = fieldType;
1715:                    iShort = isShort;
1716:                }
1717:
1718:                public int estimatePrintedLength() {
1719:                    return iShort ? 6 : 20;
1720:                }
1721:
1722:                public void printTo(StringBuffer buf, long instant,
1723:                        Chronology chrono, int displayOffset,
1724:                        DateTimeZone displayZone, Locale locale) {
1725:                    try {
1726:                        buf.append(print(instant, chrono, locale));
1727:                    } catch (RuntimeException e) {
1728:                        buf.append('\ufffd');
1729:                    }
1730:                }
1731:
1732:                public void printTo(Writer out, long instant,
1733:                        Chronology chrono, int displayOffset,
1734:                        DateTimeZone displayZone, Locale locale)
1735:                        throws IOException {
1736:                    try {
1737:                        out.write(print(instant, chrono, locale));
1738:                    } catch (RuntimeException e) {
1739:                        out.write('\ufffd');
1740:                    }
1741:                }
1742:
1743:                public void printTo(StringBuffer buf, ReadablePartial partial,
1744:                        Locale locale) {
1745:                    try {
1746:                        buf.append(print(partial, locale));
1747:                    } catch (RuntimeException e) {
1748:                        buf.append('\ufffd');
1749:                    }
1750:                }
1751:
1752:                public void printTo(Writer out, ReadablePartial partial,
1753:                        Locale locale) throws IOException {
1754:                    try {
1755:                        out.write(print(partial, locale));
1756:                    } catch (RuntimeException e) {
1757:                        out.write('\ufffd');
1758:                    }
1759:                }
1760:
1761:                private String print(long instant, Chronology chrono,
1762:                        Locale locale) {
1763:                    DateTimeField field = iFieldType.getField(chrono);
1764:                    if (iShort) {
1765:                        return field.getAsShortText(instant, locale);
1766:                    } else {
1767:                        return field.getAsText(instant, locale);
1768:                    }
1769:                }
1770:
1771:                private String print(ReadablePartial partial, Locale locale) {
1772:                    if (partial.isSupported(iFieldType)) {
1773:                        DateTimeField field = iFieldType.getField(partial
1774:                                .getChronology());
1775:                        if (iShort) {
1776:                            return field.getAsShortText(partial, locale);
1777:                        } else {
1778:                            return field.getAsText(partial, locale);
1779:                        }
1780:                    } else {
1781:                        return "\ufffd";
1782:                    }
1783:                }
1784:
1785:                public int estimateParsedLength() {
1786:                    return estimatePrintedLength();
1787:                }
1788:
1789:                public int parseInto(DateTimeParserBucket bucket, String text,
1790:                        int position) {
1791:                    Locale locale = bucket.getLocale();
1792:                    // handle languages which might have non ASCII A-Z or punctuation
1793:                    // bug 1788282
1794:                    Set validValues = null;
1795:                    int maxLength = 0;
1796:                    synchronized (cParseCache) {
1797:                        Map innerMap = (Map) cParseCache.get(locale);
1798:                        if (innerMap == null) {
1799:                            innerMap = new HashMap();
1800:                            cParseCache.put(locale, innerMap);
1801:                        }
1802:                        Object[] array = (Object[]) innerMap.get(iFieldType);
1803:                        if (array == null) {
1804:                            validValues = new HashSet(32);
1805:                            MutableDateTime dt = new MutableDateTime(0L,
1806:                                    DateTimeZone.UTC);
1807:                            Property property = dt.property(iFieldType);
1808:                            int min = property.getMinimumValueOverall();
1809:                            int max = property.getMaximumValueOverall();
1810:                            if (max - min > 32) { // protect against invalid fields
1811:                                return ~position;
1812:                            }
1813:                            maxLength = property.getMaximumTextLength(locale);
1814:                            for (int i = min; i <= max; i++) {
1815:                                property.set(i);
1816:                                validValues
1817:                                        .add(property.getAsShortText(locale));
1818:                                validValues.add(property.getAsShortText(locale)
1819:                                        .toLowerCase(locale));
1820:                                validValues.add(property.getAsShortText(locale)
1821:                                        .toUpperCase(locale));
1822:                                validValues.add(property.getAsText(locale));
1823:                                validValues.add(property.getAsText(locale)
1824:                                        .toLowerCase(locale));
1825:                                validValues.add(property.getAsText(locale)
1826:                                        .toUpperCase(locale));
1827:                            }
1828:                            if ("en".equals(locale.getLanguage())
1829:                                    && iFieldType == DateTimeFieldType.era()) {
1830:                                // hack to support for parsing "BCE" and "CE" if the language is English
1831:                                validValues.add("BCE");
1832:                                validValues.add("bce");
1833:                                validValues.add("CE");
1834:                                validValues.add("ce");
1835:                                maxLength = 3;
1836:                            }
1837:                            array = new Object[] { validValues,
1838:                                    new Integer(maxLength) };
1839:                            innerMap.put(iFieldType, array);
1840:                        } else {
1841:                            validValues = (Set) array[0];
1842:                            maxLength = ((Integer) array[1]).intValue();
1843:                        }
1844:                    }
1845:                    // match the longest string first using our knowledge of the max length
1846:                    int limit = Math.min(text.length(), position + maxLength);
1847:                    for (int i = limit; i > position; i--) {
1848:                        String match = text.substring(position, i);
1849:                        if (validValues.contains(match)) {
1850:                            bucket.saveField(iFieldType, match, locale);
1851:                            return i;
1852:                        }
1853:                    }
1854:                    return ~position;
1855:                }
1856:            }
1857:
1858:            //-----------------------------------------------------------------------
1859:            static class Fraction implements  DateTimePrinter, DateTimeParser {
1860:
1861:                private final DateTimeFieldType iFieldType;
1862:                protected int iMinDigits;
1863:                protected int iMaxDigits;
1864:
1865:                protected Fraction(DateTimeFieldType fieldType, int minDigits,
1866:                        int maxDigits) {
1867:                    super ();
1868:                    iFieldType = fieldType;
1869:                    // Limit the precision requirements.
1870:                    if (maxDigits > 18) {
1871:                        maxDigits = 18;
1872:                    }
1873:                    iMinDigits = minDigits;
1874:                    iMaxDigits = maxDigits;
1875:                }
1876:
1877:                public int estimatePrintedLength() {
1878:                    return iMaxDigits;
1879:                }
1880:
1881:                public void printTo(StringBuffer buf, long instant,
1882:                        Chronology chrono, int displayOffset,
1883:                        DateTimeZone displayZone, Locale locale) {
1884:                    try {
1885:                        printTo(buf, null, instant, chrono);
1886:                    } catch (IOException e) {
1887:                        // Not gonna happen.
1888:                    }
1889:                }
1890:
1891:                public void printTo(Writer out, long instant,
1892:                        Chronology chrono, int displayOffset,
1893:                        DateTimeZone displayZone, Locale locale)
1894:                        throws IOException {
1895:                    printTo(null, out, instant, chrono);
1896:                }
1897:
1898:                public void printTo(StringBuffer buf, ReadablePartial partial,
1899:                        Locale locale) {
1900:                    if (partial.isSupported(iFieldType)) {
1901:                        long millis = partial.getChronology().set(partial, 0L);
1902:                        try {
1903:                            printTo(buf, null, millis, partial.getChronology());
1904:                        } catch (IOException e) {
1905:                            // Not gonna happen.
1906:                        }
1907:                    } else {
1908:                        buf.append('\ufffd');
1909:                    }
1910:                }
1911:
1912:                public void printTo(Writer out, ReadablePartial partial,
1913:                        Locale locale) throws IOException {
1914:                    if (partial.isSupported(iFieldType)) {
1915:                        long millis = partial.getChronology().set(partial, 0L);
1916:                        printTo(null, out, millis, partial.getChronology());
1917:                    } else {
1918:                        out.write('\ufffd');
1919:                    }
1920:                }
1921:
1922:                protected void printTo(StringBuffer buf, Writer out,
1923:                        long instant, Chronology chrono) throws IOException {
1924:                    DateTimeField field = iFieldType.getField(chrono);
1925:                    int minDigits = iMinDigits;
1926:
1927:                    long fraction;
1928:                    try {
1929:                        fraction = field.remainder(instant);
1930:                    } catch (RuntimeException e) {
1931:                        if (buf != null) {
1932:                            appendUnknownString(buf, minDigits);
1933:                        } else {
1934:                            printUnknownString(out, minDigits);
1935:                        }
1936:                        return;
1937:                    }
1938:
1939:                    if (fraction == 0) {
1940:                        if (buf != null) {
1941:                            while (--minDigits >= 0) {
1942:                                buf.append('0');
1943:                            }
1944:                        } else {
1945:                            while (--minDigits >= 0) {
1946:                                out.write('0');
1947:                            }
1948:                        }
1949:                        return;
1950:                    }
1951:
1952:                    String str;
1953:                    long[] fractionData = getFractionData(fraction, field);
1954:                    long scaled = fractionData[0];
1955:                    int maxDigits = (int) fractionData[1];
1956:
1957:                    if ((scaled & 0x7fffffff) == scaled) {
1958:                        str = Integer.toString((int) scaled);
1959:                    } else {
1960:                        str = Long.toString(scaled);
1961:                    }
1962:
1963:                    int length = str.length();
1964:                    int digits = maxDigits;
1965:                    while (length < digits) {
1966:                        if (buf != null) {
1967:                            buf.append('0');
1968:                        } else {
1969:                            out.write('0');
1970:                        }
1971:                        minDigits--;
1972:                        digits--;
1973:                    }
1974:
1975:                    if (minDigits < digits) {
1976:                        // Chop off as many trailing zero digits as necessary.
1977:                        while (minDigits < digits) {
1978:                            if (length <= 1 || str.charAt(length - 1) != '0') {
1979:                                break;
1980:                            }
1981:                            digits--;
1982:                            length--;
1983:                        }
1984:                        if (length < str.length()) {
1985:                            if (buf != null) {
1986:                                for (int i = 0; i < length; i++) {
1987:                                    buf.append(str.charAt(i));
1988:                                }
1989:                            } else {
1990:                                for (int i = 0; i < length; i++) {
1991:                                    out.write(str.charAt(i));
1992:                                }
1993:                            }
1994:                            return;
1995:                        }
1996:                    }
1997:
1998:                    if (buf != null) {
1999:                        buf.append(str);
2000:                    } else {
2001:                        out.write(str);
2002:                    }
2003:                }
2004:
2005:                private long[] getFractionData(long fraction,
2006:                        DateTimeField field) {
2007:                    long rangeMillis = field.getDurationField().getUnitMillis();
2008:                    long scalar;
2009:                    int maxDigits = iMaxDigits;
2010:                    while (true) {
2011:                        switch (maxDigits) {
2012:                        default:
2013:                            scalar = 1L;
2014:                            break;
2015:                        case 1:
2016:                            scalar = 10L;
2017:                            break;
2018:                        case 2:
2019:                            scalar = 100L;
2020:                            break;
2021:                        case 3:
2022:                            scalar = 1000L;
2023:                            break;
2024:                        case 4:
2025:                            scalar = 10000L;
2026:                            break;
2027:                        case 5:
2028:                            scalar = 100000L;
2029:                            break;
2030:                        case 6:
2031:                            scalar = 1000000L;
2032:                            break;
2033:                        case 7:
2034:                            scalar = 10000000L;
2035:                            break;
2036:                        case 8:
2037:                            scalar = 100000000L;
2038:                            break;
2039:                        case 9:
2040:                            scalar = 1000000000L;
2041:                            break;
2042:                        case 10:
2043:                            scalar = 10000000000L;
2044:                            break;
2045:                        case 11:
2046:                            scalar = 100000000000L;
2047:                            break;
2048:                        case 12:
2049:                            scalar = 1000000000000L;
2050:                            break;
2051:                        case 13:
2052:                            scalar = 10000000000000L;
2053:                            break;
2054:                        case 14:
2055:                            scalar = 100000000000000L;
2056:                            break;
2057:                        case 15:
2058:                            scalar = 1000000000000000L;
2059:                            break;
2060:                        case 16:
2061:                            scalar = 10000000000000000L;
2062:                            break;
2063:                        case 17:
2064:                            scalar = 100000000000000000L;
2065:                            break;
2066:                        case 18:
2067:                            scalar = 1000000000000000000L;
2068:                            break;
2069:                        }
2070:                        if (((rangeMillis * scalar) / scalar) == rangeMillis) {
2071:                            break;
2072:                        }
2073:                        // Overflowed: scale down.
2074:                        maxDigits--;
2075:                    }
2076:
2077:                    return new long[] { fraction * scalar / rangeMillis,
2078:                            maxDigits };
2079:                }
2080:
2081:                public int estimateParsedLength() {
2082:                    return iMaxDigits;
2083:                }
2084:
2085:                public int parseInto(DateTimeParserBucket bucket, String text,
2086:                        int position) {
2087:                    DateTimeField field = iFieldType.getField(bucket
2088:                            .getChronology());
2089:
2090:                    int limit = Math.min(iMaxDigits, text.length() - position);
2091:
2092:                    long value = 0;
2093:                    long n = field.getDurationField().getUnitMillis() * 10;
2094:                    int length = 0;
2095:                    while (length < limit) {
2096:                        char c = text.charAt(position + length);
2097:                        if (c < '0' || c > '9') {
2098:                            break;
2099:                        }
2100:                        length++;
2101:                        long nn = n / 10;
2102:                        value += (c - '0') * nn;
2103:                        n = nn;
2104:                    }
2105:
2106:                    value /= 10;
2107:
2108:                    if (length == 0) {
2109:                        return ~position;
2110:                    }
2111:
2112:                    if (value > Integer.MAX_VALUE) {
2113:                        return ~position;
2114:                    }
2115:
2116:                    DateTimeField parseField = new PreciseDateTimeField(
2117:                            DateTimeFieldType.millisOfSecond(),
2118:                            MillisDurationField.INSTANCE, field
2119:                                    .getDurationField());
2120:
2121:                    bucket.saveField(parseField, (int) value);
2122:
2123:                    return position + length;
2124:                }
2125:            }
2126:
2127:            //-----------------------------------------------------------------------
2128:            static class TimeZoneOffset implements  DateTimePrinter,
2129:                    DateTimeParser {
2130:
2131:                private final String iZeroOffsetText;
2132:                private final boolean iShowSeparators;
2133:                private final int iMinFields;
2134:                private final int iMaxFields;
2135:
2136:                TimeZoneOffset(String zeroOffsetText, boolean showSeparators,
2137:                        int minFields, int maxFields) {
2138:                    super ();
2139:                    iZeroOffsetText = zeroOffsetText;
2140:                    iShowSeparators = showSeparators;
2141:                    if (minFields <= 0 || maxFields < minFields) {
2142:                        throw new IllegalArgumentException();
2143:                    }
2144:                    if (minFields > 4) {
2145:                        minFields = 4;
2146:                        maxFields = 4;
2147:                    }
2148:                    iMinFields = minFields;
2149:                    iMaxFields = maxFields;
2150:                }
2151:
2152:                public int estimatePrintedLength() {
2153:                    int est = 1 + iMinFields << 1;
2154:                    if (iShowSeparators) {
2155:                        est += iMinFields - 1;
2156:                    }
2157:                    if (iZeroOffsetText != null
2158:                            && iZeroOffsetText.length() > est) {
2159:                        est = iZeroOffsetText.length();
2160:                    }
2161:                    return est;
2162:                }
2163:
2164:                public void printTo(StringBuffer buf, long instant,
2165:                        Chronology chrono, int displayOffset,
2166:                        DateTimeZone displayZone, Locale locale) {
2167:                    if (displayZone == null) {
2168:                        return; // no zone
2169:                    }
2170:                    if (displayOffset == 0 && iZeroOffsetText != null) {
2171:                        buf.append(iZeroOffsetText);
2172:                        return;
2173:                    }
2174:                    if (displayOffset >= 0) {
2175:                        buf.append('+');
2176:                    } else {
2177:                        buf.append('-');
2178:                        displayOffset = -displayOffset;
2179:                    }
2180:
2181:                    int hours = displayOffset
2182:                            / DateTimeConstants.MILLIS_PER_HOUR;
2183:                    FormatUtils.appendPaddedInteger(buf, hours, 2);
2184:                    if (iMaxFields == 1) {
2185:                        return;
2186:                    }
2187:                    displayOffset -= hours
2188:                            * (int) DateTimeConstants.MILLIS_PER_HOUR;
2189:                    if (displayOffset == 0 && iMinFields <= 1) {
2190:                        return;
2191:                    }
2192:
2193:                    int minutes = displayOffset
2194:                            / DateTimeConstants.MILLIS_PER_MINUTE;
2195:                    if (iShowSeparators) {
2196:                        buf.append(':');
2197:                    }
2198:                    FormatUtils.appendPaddedInteger(buf, minutes, 2);
2199:                    if (iMaxFields == 2) {
2200:                        return;
2201:                    }
2202:                    displayOffset -= minutes
2203:                            * DateTimeConstants.MILLIS_PER_MINUTE;
2204:                    if (displayOffset == 0 && iMinFields <= 2) {
2205:                        return;
2206:                    }
2207:
2208:                    int seconds = displayOffset
2209:                            / DateTimeConstants.MILLIS_PER_SECOND;
2210:                    if (iShowSeparators) {
2211:                        buf.append(':');
2212:                    }
2213:                    FormatUtils.appendPaddedInteger(buf, seconds, 2);
2214:                    if (iMaxFields == 3) {
2215:                        return;
2216:                    }
2217:                    displayOffset -= seconds
2218:                            * DateTimeConstants.MILLIS_PER_SECOND;
2219:                    if (displayOffset == 0 && iMinFields <= 3) {
2220:                        return;
2221:                    }
2222:
2223:                    if (iShowSeparators) {
2224:                        buf.append('.');
2225:                    }
2226:                    FormatUtils.appendPaddedInteger(buf, displayOffset, 3);
2227:                }
2228:
2229:                public void printTo(Writer out, long instant,
2230:                        Chronology chrono, int displayOffset,
2231:                        DateTimeZone displayZone, Locale locale)
2232:                        throws IOException {
2233:                    if (displayZone == null) {
2234:                        return; // no zone
2235:                    }
2236:                    if (displayOffset == 0 && iZeroOffsetText != null) {
2237:                        out.write(iZeroOffsetText);
2238:                        return;
2239:                    }
2240:                    if (displayOffset >= 0) {
2241:                        out.write('+');
2242:                    } else {
2243:                        out.write('-');
2244:                        displayOffset = -displayOffset;
2245:                    }
2246:
2247:                    int hours = displayOffset
2248:                            / DateTimeConstants.MILLIS_PER_HOUR;
2249:                    FormatUtils.writePaddedInteger(out, hours, 2);
2250:                    if (iMaxFields == 1) {
2251:                        return;
2252:                    }
2253:                    displayOffset -= hours
2254:                            * (int) DateTimeConstants.MILLIS_PER_HOUR;
2255:                    if (displayOffset == 0 && iMinFields == 1) {
2256:                        return;
2257:                    }
2258:
2259:                    int minutes = displayOffset
2260:                            / DateTimeConstants.MILLIS_PER_MINUTE;
2261:                    if (iShowSeparators) {
2262:                        out.write(':');
2263:                    }
2264:                    FormatUtils.writePaddedInteger(out, minutes, 2);
2265:                    if (iMaxFields == 2) {
2266:                        return;
2267:                    }
2268:                    displayOffset -= minutes
2269:                            * DateTimeConstants.MILLIS_PER_MINUTE;
2270:                    if (displayOffset == 0 && iMinFields == 2) {
2271:                        return;
2272:                    }
2273:
2274:                    int seconds = displayOffset
2275:                            / DateTimeConstants.MILLIS_PER_SECOND;
2276:                    if (iShowSeparators) {
2277:                        out.write(':');
2278:                    }
2279:                    FormatUtils.writePaddedInteger(out, seconds, 2);
2280:                    if (iMaxFields == 3) {
2281:                        return;
2282:                    }
2283:                    displayOffset -= seconds
2284:                            * DateTimeConstants.MILLIS_PER_SECOND;
2285:                    if (displayOffset == 0 && iMinFields == 3) {
2286:                        return;
2287:                    }
2288:
2289:                    if (iShowSeparators) {
2290:                        out.write('.');
2291:                    }
2292:                    FormatUtils.writePaddedInteger(out, displayOffset, 3);
2293:                }
2294:
2295:                public void printTo(StringBuffer buf, ReadablePartial partial,
2296:                        Locale locale) {
2297:                    // no zone info
2298:                }
2299:
2300:                public void printTo(Writer out, ReadablePartial partial,
2301:                        Locale locale) throws IOException {
2302:                    // no zone info
2303:                }
2304:
2305:                public int estimateParsedLength() {
2306:                    return estimatePrintedLength();
2307:                }
2308:
2309:                public int parseInto(DateTimeParserBucket bucket, String text,
2310:                        int position) {
2311:                    int limit = text.length() - position;
2312:
2313:                    zeroOffset: if (iZeroOffsetText != null) {
2314:                        if (iZeroOffsetText.length() == 0) {
2315:                            // Peek ahead, looking for sign character.
2316:                            if (limit > 0) {
2317:                                char c = text.charAt(position);
2318:                                if (c == '-' || c == '+') {
2319:                                    break zeroOffset;
2320:                                }
2321:                            }
2322:                            bucket.setOffset(0);
2323:                            return position;
2324:                        }
2325:                        if (text.regionMatches(true, position, iZeroOffsetText,
2326:                                0, iZeroOffsetText.length())) {
2327:                            bucket.setOffset(0);
2328:                            return position + iZeroOffsetText.length();
2329:                        }
2330:                    }
2331:
2332:                    // Format to expect is sign character followed by at least one digit.
2333:
2334:                    if (limit <= 1) {
2335:                        return ~position;
2336:                    }
2337:
2338:                    boolean negative;
2339:                    char c = text.charAt(position);
2340:                    if (c == '-') {
2341:                        negative = true;
2342:                    } else if (c == '+') {
2343:                        negative = false;
2344:                    } else {
2345:                        return ~position;
2346:                    }
2347:
2348:                    limit--;
2349:                    position++;
2350:
2351:                    // Format following sign is one of:
2352:                    //
2353:                    // hh
2354:                    // hhmm
2355:                    // hhmmss
2356:                    // hhmmssSSS
2357:                    // hh:mm
2358:                    // hh:mm:ss
2359:                    // hh:mm:ss.SSS
2360:
2361:                    // First parse hours.
2362:
2363:                    if (digitCount(text, position, 2) < 2) {
2364:                        // Need two digits for hour.
2365:                        return ~position;
2366:                    }
2367:
2368:                    int offset;
2369:
2370:                    int hours = FormatUtils.parseTwoDigits(text, position);
2371:                    if (hours > 23) {
2372:                        return ~position;
2373:                    }
2374:                    offset = hours * DateTimeConstants.MILLIS_PER_HOUR;
2375:                    limit -= 2;
2376:                    position += 2;
2377:
2378:                    parse: {
2379:                        // Need to decide now if separators are expected or parsing
2380:                        // stops at hour field.
2381:
2382:                        if (limit <= 0) {
2383:                            break parse;
2384:                        }
2385:
2386:                        boolean expectSeparators;
2387:                        c = text.charAt(position);
2388:                        if (c == ':') {
2389:                            expectSeparators = true;
2390:                            limit--;
2391:                            position++;
2392:                        } else if (c >= '0' && c <= '9') {
2393:                            expectSeparators = false;
2394:                        } else {
2395:                            break parse;
2396:                        }
2397:
2398:                        // Proceed to parse minutes.
2399:
2400:                        int count = digitCount(text, position, 2);
2401:                        if (count == 0 && !expectSeparators) {
2402:                            break parse;
2403:                        } else if (count < 2) {
2404:                            // Need two digits for minute.
2405:                            return ~position;
2406:                        }
2407:
2408:                        int minutes = FormatUtils
2409:                                .parseTwoDigits(text, position);
2410:                        if (minutes > 59) {
2411:                            return ~position;
2412:                        }
2413:                        offset += minutes * DateTimeConstants.MILLIS_PER_MINUTE;
2414:                        limit -= 2;
2415:                        position += 2;
2416:
2417:                        // Proceed to parse seconds.
2418:
2419:                        if (limit <= 0) {
2420:                            break parse;
2421:                        }
2422:
2423:                        if (expectSeparators) {
2424:                            if (text.charAt(position) != ':') {
2425:                                break parse;
2426:                            }
2427:                            limit--;
2428:                            position++;
2429:                        }
2430:
2431:                        count = digitCount(text, position, 2);
2432:                        if (count == 0 && !expectSeparators) {
2433:                            break parse;
2434:                        } else if (count < 2) {
2435:                            // Need two digits for second.
2436:                            return ~position;
2437:                        }
2438:
2439:                        int seconds = FormatUtils
2440:                                .parseTwoDigits(text, position);
2441:                        if (seconds > 59) {
2442:                            return ~position;
2443:                        }
2444:                        offset += seconds * DateTimeConstants.MILLIS_PER_SECOND;
2445:                        limit -= 2;
2446:                        position += 2;
2447:
2448:                        // Proceed to parse fraction of second.
2449:
2450:                        if (limit <= 0) {
2451:                            break parse;
2452:                        }
2453:
2454:                        if (expectSeparators) {
2455:                            if (text.charAt(position) != '.'
2456:                                    && text.charAt(position) != ',') {
2457:                                break parse;
2458:                            }
2459:                            limit--;
2460:                            position++;
2461:                        }
2462:
2463:                        count = digitCount(text, position, 3);
2464:                        if (count == 0 && !expectSeparators) {
2465:                            break parse;
2466:                        } else if (count < 1) {
2467:                            // Need at least one digit for fraction of second.
2468:                            return ~position;
2469:                        }
2470:
2471:                        offset += (text.charAt(position++) - '0') * 100;
2472:                        if (count > 1) {
2473:                            offset += (text.charAt(position++) - '0') * 10;
2474:                            if (count > 2) {
2475:                                offset += text.charAt(position++) - '0';
2476:                            }
2477:                        }
2478:                    }
2479:
2480:                    bucket.setOffset(negative ? -offset : offset);
2481:                    return position;
2482:                }
2483:
2484:                /**
2485:                 * Returns actual amount of digits to parse, but no more than original
2486:                 * 'amount' parameter.
2487:                 */
2488:                private int digitCount(String text, int position, int amount) {
2489:                    int limit = Math.min(text.length() - position, amount);
2490:                    amount = 0;
2491:                    for (; limit > 0; limit--) {
2492:                        char c = text.charAt(position + amount);
2493:                        if (c < '0' || c > '9') {
2494:                            break;
2495:                        }
2496:                        amount++;
2497:                    }
2498:                    return amount;
2499:                }
2500:            }
2501:
2502:            //-----------------------------------------------------------------------
2503:            static class TimeZoneName implements  DateTimePrinter {
2504:
2505:                static final int LONG_NAME = 0;
2506:                static final int SHORT_NAME = 1;
2507:                static final int ID = 2;
2508:
2509:                private final int iType;
2510:
2511:                TimeZoneName(int type) {
2512:                    super ();
2513:                    iType = type;
2514:                }
2515:
2516:                public int estimatePrintedLength() {
2517:                    return (iType == SHORT_NAME ? 4 : 20);
2518:                }
2519:
2520:                public void printTo(StringBuffer buf, long instant,
2521:                        Chronology chrono, int displayOffset,
2522:                        DateTimeZone displayZone, Locale locale) {
2523:                    buf.append(print(instant - displayOffset, displayZone,
2524:                            locale));
2525:                }
2526:
2527:                public void printTo(Writer out, long instant,
2528:                        Chronology chrono, int displayOffset,
2529:                        DateTimeZone displayZone, Locale locale)
2530:                        throws IOException {
2531:                    out.write(print(instant - displayOffset, displayZone,
2532:                            locale));
2533:                }
2534:
2535:                private String print(long instant, DateTimeZone displayZone,
2536:                        Locale locale) {
2537:                    if (displayZone == null) {
2538:                        return ""; // no zone
2539:                    }
2540:                    switch (iType) {
2541:                    case LONG_NAME:
2542:                        return displayZone.getName(instant, locale);
2543:                    case SHORT_NAME:
2544:                        return displayZone.getShortName(instant, locale);
2545:                    case ID:
2546:                        return displayZone.getID();
2547:                    }
2548:                    return "";
2549:                }
2550:
2551:                public void printTo(StringBuffer buf, ReadablePartial partial,
2552:                        Locale locale) {
2553:                    // no zone info
2554:                }
2555:
2556:                public void printTo(Writer out, ReadablePartial partial,
2557:                        Locale locale) throws IOException {
2558:                    // no zone info
2559:                }
2560:            }
2561:
2562:            //-----------------------------------------------------------------------
2563:            static class Composite implements  DateTimePrinter, DateTimeParser {
2564:
2565:                private final DateTimePrinter[] iPrinters;
2566:                private final DateTimeParser[] iParsers;
2567:
2568:                private final int iPrintedLengthEstimate;
2569:                private final int iParsedLengthEstimate;
2570:
2571:                Composite(List elementPairs) {
2572:                    super ();
2573:
2574:                    List printerList = new ArrayList();
2575:                    List parserList = new ArrayList();
2576:
2577:                    decompose(elementPairs, printerList, parserList);
2578:
2579:                    if (printerList.size() <= 0) {
2580:                        iPrinters = null;
2581:                        iPrintedLengthEstimate = 0;
2582:                    } else {
2583:                        int size = printerList.size();
2584:                        iPrinters = new DateTimePrinter[size];
2585:                        int printEst = 0;
2586:                        for (int i = 0; i < size; i++) {
2587:                            DateTimePrinter printer = (DateTimePrinter) printerList
2588:                                    .get(i);
2589:                            printEst += printer.estimatePrintedLength();
2590:                            iPrinters[i] = printer;
2591:                        }
2592:                        iPrintedLengthEstimate = printEst;
2593:                    }
2594:
2595:                    if (parserList.size() <= 0) {
2596:                        iParsers = null;
2597:                        iParsedLengthEstimate = 0;
2598:                    } else {
2599:                        int size = parserList.size();
2600:                        iParsers = new DateTimeParser[size];
2601:                        int parseEst = 0;
2602:                        for (int i = 0; i < size; i++) {
2603:                            DateTimeParser parser = (DateTimeParser) parserList
2604:                                    .get(i);
2605:                            parseEst += parser.estimateParsedLength();
2606:                            iParsers[i] = parser;
2607:                        }
2608:                        iParsedLengthEstimate = parseEst;
2609:                    }
2610:                }
2611:
2612:                public int estimatePrintedLength() {
2613:                    return iPrintedLengthEstimate;
2614:                }
2615:
2616:                public void printTo(StringBuffer buf, long instant,
2617:                        Chronology chrono, int displayOffset,
2618:                        DateTimeZone displayZone, Locale locale) {
2619:                    DateTimePrinter[] elements = iPrinters;
2620:                    if (elements == null) {
2621:                        throw new UnsupportedOperationException();
2622:                    }
2623:
2624:                    if (locale == null) {
2625:                        // Guard against default locale changing concurrently.
2626:                        locale = Locale.getDefault();
2627:                    }
2628:
2629:                    int len = elements.length;
2630:                    for (int i = 0; i < len; i++) {
2631:                        elements[i].printTo(buf, instant, chrono,
2632:                                displayOffset, displayZone, locale);
2633:                    }
2634:                }
2635:
2636:                public void printTo(Writer out, long instant,
2637:                        Chronology chrono, int displayOffset,
2638:                        DateTimeZone displayZone, Locale locale)
2639:                        throws IOException {
2640:                    DateTimePrinter[] elements = iPrinters;
2641:                    if (elements == null) {
2642:                        throw new UnsupportedOperationException();
2643:                    }
2644:
2645:                    if (locale == null) {
2646:                        // Guard against default locale changing concurrently.
2647:                        locale = Locale.getDefault();
2648:                    }
2649:
2650:                    int len = elements.length;
2651:                    for (int i = 0; i < len; i++) {
2652:                        elements[i].printTo(out, instant, chrono,
2653:                                displayOffset, displayZone, locale);
2654:                    }
2655:                }
2656:
2657:                public void printTo(StringBuffer buf, ReadablePartial partial,
2658:                        Locale locale) {
2659:                    DateTimePrinter[] elements = iPrinters;
2660:                    if (elements == null) {
2661:                        throw new UnsupportedOperationException();
2662:                    }
2663:
2664:                    if (locale == null) {
2665:                        // Guard against default locale changing concurrently.
2666:                        locale = Locale.getDefault();
2667:                    }
2668:
2669:                    int len = elements.length;
2670:                    for (int i = 0; i < len; i++) {
2671:                        elements[i].printTo(buf, partial, locale);
2672:                    }
2673:                }
2674:
2675:                public void printTo(Writer out, ReadablePartial partial,
2676:                        Locale locale) throws IOException {
2677:                    DateTimePrinter[] elements = iPrinters;
2678:                    if (elements == null) {
2679:                        throw new UnsupportedOperationException();
2680:                    }
2681:
2682:                    if (locale == null) {
2683:                        // Guard against default locale changing concurrently.
2684:                        locale = Locale.getDefault();
2685:                    }
2686:
2687:                    int len = elements.length;
2688:                    for (int i = 0; i < len; i++) {
2689:                        elements[i].printTo(out, partial, locale);
2690:                    }
2691:                }
2692:
2693:                public int estimateParsedLength() {
2694:                    return iParsedLengthEstimate;
2695:                }
2696:
2697:                public int parseInto(DateTimeParserBucket bucket, String text,
2698:                        int position) {
2699:                    DateTimeParser[] elements = iParsers;
2700:                    if (elements == null) {
2701:                        throw new UnsupportedOperationException();
2702:                    }
2703:
2704:                    int len = elements.length;
2705:                    for (int i = 0; i < len && position >= 0; i++) {
2706:                        position = elements[i]
2707:                                .parseInto(bucket, text, position);
2708:                    }
2709:                    return position;
2710:                }
2711:
2712:                boolean isPrinter() {
2713:                    return iPrinters != null;
2714:                }
2715:
2716:                boolean isParser() {
2717:                    return iParsers != null;
2718:                }
2719:
2720:                /**
2721:                 * Processes the element pairs, putting results into the given printer
2722:                 * and parser lists.
2723:                 */
2724:                private void decompose(List elementPairs, List printerList,
2725:                        List parserList) {
2726:                    int size = elementPairs.size();
2727:                    for (int i = 0; i < size; i += 2) {
2728:                        Object element = elementPairs.get(i);
2729:                        if (element instanceof  DateTimePrinter) {
2730:                            if (element instanceof  Composite) {
2731:                                addArrayToList(printerList,
2732:                                        ((Composite) element).iPrinters);
2733:                            } else {
2734:                                printerList.add(element);
2735:                            }
2736:                        }
2737:
2738:                        element = elementPairs.get(i + 1);
2739:                        if (element instanceof  DateTimeParser) {
2740:                            if (element instanceof  Composite) {
2741:                                addArrayToList(parserList,
2742:                                        ((Composite) element).iParsers);
2743:                            } else {
2744:                                parserList.add(element);
2745:                            }
2746:                        }
2747:                    }
2748:                }
2749:
2750:                private void addArrayToList(List list, Object[] array) {
2751:                    if (array != null) {
2752:                        for (int i = 0; i < array.length; i++) {
2753:                            list.add(array[i]);
2754:                        }
2755:                    }
2756:                }
2757:            }
2758:
2759:            //-----------------------------------------------------------------------
2760:            static class MatchingParser implements  DateTimeParser {
2761:
2762:                private final DateTimeParser[] iParsers;
2763:                private final int iParsedLengthEstimate;
2764:
2765:                MatchingParser(DateTimeParser[] parsers) {
2766:                    super ();
2767:                    iParsers = parsers;
2768:                    int est = 0;
2769:                    for (int i = parsers.length; --i >= 0;) {
2770:                        DateTimeParser parser = parsers[i];
2771:                        if (parser != null) {
2772:                            int len = parser.estimateParsedLength();
2773:                            if (len > est) {
2774:                                est = len;
2775:                            }
2776:                        }
2777:                    }
2778:                    iParsedLengthEstimate = est;
2779:                }
2780:
2781:                public int estimateParsedLength() {
2782:                    return iParsedLengthEstimate;
2783:                }
2784:
2785:                public int parseInto(DateTimeParserBucket bucket, String text,
2786:                        int position) {
2787:                    DateTimeParser[] parsers = iParsers;
2788:                    int length = parsers.length;
2789:
2790:                    final Object originalState = bucket.saveState();
2791:                    boolean isOptional = false;
2792:
2793:                    int bestValidPos = position;
2794:                    Object bestValidState = null;
2795:
2796:                    int bestInvalidPos = position;
2797:
2798:                    for (int i = 0; i < length; i++) {
2799:                        DateTimeParser parser = parsers[i];
2800:                        if (parser == null) {
2801:                            // The empty parser wins only if nothing is better.
2802:                            if (bestValidPos <= position) {
2803:                                return position;
2804:                            }
2805:                            isOptional = true;
2806:                            break;
2807:                        }
2808:                        int parsePos = parser.parseInto(bucket, text, position);
2809:                        if (parsePos >= position) {
2810:                            if (parsePos > bestValidPos) {
2811:                                if (parsePos >= text.length()
2812:                                        || (i + 1) >= length
2813:                                        || parsers[i + 1] == null) {
2814:
2815:                                    // Completely parsed text or no more parsers to
2816:                                    // check. Skip the rest.
2817:                                    return parsePos;
2818:                                }
2819:                                bestValidPos = parsePos;
2820:                                bestValidState = bucket.saveState();
2821:                            }
2822:                        } else {
2823:                            if (parsePos < 0) {
2824:                                parsePos = ~parsePos;
2825:                                if (parsePos > bestInvalidPos) {
2826:                                    bestInvalidPos = parsePos;
2827:                                }
2828:                            }
2829:                        }
2830:                        bucket.restoreState(originalState);
2831:                    }
2832:
2833:                    if (bestValidPos > position
2834:                            || (bestValidPos == position && isOptional)) {
2835:                        // Restore the state to the best valid parse.
2836:                        if (bestValidState != null) {
2837:                            bucket.restoreState(bestValidState);
2838:                        }
2839:                        return bestValidPos;
2840:                    }
2841:
2842:                    return ~bestInvalidPos;
2843:                }
2844:            }
2845:
2846:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.