Source Code Cross Referenced for PeriodFormatterBuilder.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-2006 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.Collections;
0022:        import java.util.List;
0023:        import java.util.Locale;
0024:        import java.util.TreeSet;
0025:
0026:        import org.joda.time.DateTimeConstants;
0027:        import org.joda.time.DurationFieldType;
0028:        import org.joda.time.PeriodType;
0029:        import org.joda.time.ReadWritablePeriod;
0030:        import org.joda.time.ReadablePeriod;
0031:
0032:        /**
0033:         * Factory that creates complex instances of PeriodFormatter via method calls.
0034:         * <p>
0035:         * Period formatting is performed by the {@link PeriodFormatter} class.
0036:         * Three classes provide factory methods to create formatters, and this is one.
0037:         * The others are {@link PeriodFormat} and {@link ISOPeriodFormat}.
0038:         * <p>
0039:         * PeriodFormatterBuilder is used for constructing formatters which are then
0040:         * used to print or parse. The formatters are built by appending specific fields
0041:         * or other formatters to an instance of this builder.
0042:         * <p>
0043:         * For example, a formatter that prints years and months, like "15 years and 8 months",
0044:         * can be constructed as follows:
0045:         * <p>
0046:         * <pre>
0047:         * PeriodFormatter yearsAndMonths = new PeriodFormatterBuilder()
0048:         *     .printZeroAlways()
0049:         *     .appendYears()
0050:         *     .appendSuffix(" year", " years")
0051:         *     .appendSeparator(" and ")
0052:         *     .printZeroRarely()
0053:         *     .appendMonths()
0054:         *     .appendSuffix(" month", " months")
0055:         *     .toFormatter();
0056:         * </pre>
0057:         * <p>
0058:         * PeriodFormatterBuilder itself is mutable and not thread-safe, but the
0059:         * formatters that it builds are thread-safe and immutable.
0060:         *
0061:         * @author Brian S O'Neill
0062:         * @since 1.0
0063:         * @see PeriodFormat
0064:         */
0065:        public class PeriodFormatterBuilder {
0066:            private static final int PRINT_ZERO_RARELY_FIRST = 1;
0067:            private static final int PRINT_ZERO_RARELY_LAST = 2;
0068:            private static final int PRINT_ZERO_IF_SUPPORTED = 3;
0069:            private static final int PRINT_ZERO_ALWAYS = 4;
0070:            private static final int PRINT_ZERO_NEVER = 5;
0071:
0072:            private static final int YEARS = 0;
0073:            private static final int MONTHS = 1;
0074:            private static final int WEEKS = 2;
0075:            private static final int DAYS = 3;
0076:            private static final int HOURS = 4;
0077:            private static final int MINUTES = 5;
0078:            private static final int SECONDS = 6;
0079:            private static final int MILLIS = 7;
0080:            private static final int SECONDS_MILLIS = 8;
0081:            private static final int SECONDS_OPTIONAL_MILLIS = 9;
0082:            private static final int MAX_FIELD = SECONDS_OPTIONAL_MILLIS;
0083:
0084:            private int iMinPrintedDigits;
0085:            private int iPrintZeroSetting;
0086:            private int iMaxParsedDigits;
0087:            private boolean iRejectSignedValues;
0088:
0089:            private PeriodFieldAffix iPrefix;
0090:
0091:            // List of Printers and Parsers used to build a final formatter.
0092:            private List iElementPairs;
0093:            /** Set to true if the formatter is not a printer. */
0094:            private boolean iNotPrinter;
0095:            /** Set to true if the formatter is not a parser. */
0096:            private boolean iNotParser;
0097:
0098:            // Last PeriodFormatter appended of each field type.
0099:            private FieldFormatter[] iFieldFormatters;
0100:
0101:            public PeriodFormatterBuilder() {
0102:                clear();
0103:            }
0104:
0105:            //-----------------------------------------------------------------------
0106:            /**
0107:             * Constructs a PeriodFormatter using all the appended elements.
0108:             * <p>
0109:             * This is the main method used by applications at the end of the build
0110:             * process to create a usable formatter.
0111:             * <p>
0112:             * Subsequent changes to this builder do not affect the returned formatter.
0113:             * <p>
0114:             * The returned formatter may not support both printing and parsing.
0115:             * The methods {@link PeriodFormatter#isPrinter()} and
0116:             * {@link PeriodFormatter#isParser()} will help you determine the state
0117:             * of the formatter.
0118:             * 
0119:             * @return the newly created formatter
0120:             * @throws IllegalStateException if the builder can produce neither a printer nor a parser
0121:             */
0122:            public PeriodFormatter toFormatter() {
0123:                PeriodFormatter formatter = toFormatter(iElementPairs,
0124:                        iNotPrinter, iNotParser);
0125:                iFieldFormatters = (FieldFormatter[]) iFieldFormatters.clone();
0126:                return formatter;
0127:            }
0128:
0129:            /**
0130:             * Internal method to create a PeriodPrinter instance using all the
0131:             * appended elements.
0132:             * <p>
0133:             * Most applications will not use this method.
0134:             * If you want a printer in an application, call {@link #toFormatter()}
0135:             * and just use the printing API.
0136:             * <p>
0137:             * Subsequent changes to this builder do not affect the returned printer.
0138:             * 
0139:             * @return the newly created printer, null if builder cannot create a printer
0140:             */
0141:            public PeriodPrinter toPrinter() {
0142:                if (iNotPrinter) {
0143:                    return null;
0144:                }
0145:                return toFormatter().getPrinter();
0146:            }
0147:
0148:            /**
0149:             * Internal method to create a PeriodParser instance using all the
0150:             * appended elements.
0151:             * <p>
0152:             * Most applications will not use this method.
0153:             * If you want a printer in an application, call {@link #toFormatter()}
0154:             * and just use the printing API.
0155:             * <p>
0156:             * Subsequent changes to this builder do not affect the returned parser.
0157:             * 
0158:             * @return the newly created parser, null if builder cannot create a parser
0159:             */
0160:            public PeriodParser toParser() {
0161:                if (iNotParser) {
0162:                    return null;
0163:                }
0164:                return toFormatter().getParser();
0165:            }
0166:
0167:            //-----------------------------------------------------------------------
0168:            /**
0169:             * Clears out all the appended elements, allowing this builder to be reused.
0170:             */
0171:            public void clear() {
0172:                iMinPrintedDigits = 1;
0173:                iPrintZeroSetting = PRINT_ZERO_RARELY_LAST;
0174:                iMaxParsedDigits = 10;
0175:                iRejectSignedValues = false;
0176:                iPrefix = null;
0177:                if (iElementPairs == null) {
0178:                    iElementPairs = new ArrayList();
0179:                } else {
0180:                    iElementPairs.clear();
0181:                }
0182:                iNotPrinter = false;
0183:                iNotParser = false;
0184:                iFieldFormatters = new FieldFormatter[10];
0185:            }
0186:
0187:            /**
0188:             * Appends another formatter.
0189:             *
0190:             * @return this PeriodFormatterBuilder
0191:             */
0192:            public PeriodFormatterBuilder append(PeriodFormatter formatter) {
0193:                if (formatter == null) {
0194:                    throw new IllegalArgumentException("No formatter supplied");
0195:                }
0196:                clearPrefix();
0197:                append0(formatter.getPrinter(), formatter.getParser());
0198:                return this ;
0199:            }
0200:
0201:            /**
0202:             * Appends a printer parser pair.
0203:             * <p>
0204:             * Either the printer or the parser may be null, in which case the builder will
0205:             * be unable to produce a parser or printer repectively.
0206:             *
0207:             * @param printer  appends a printer to the builder, null if printing is not supported
0208:             * @param parser  appends a parser to the builder, null if parsing is not supported
0209:             * @return this PeriodFormatterBuilder
0210:             * @throws IllegalArgumentException if both the printer and parser are null
0211:             */
0212:            public PeriodFormatterBuilder append(PeriodPrinter printer,
0213:                    PeriodParser parser) {
0214:                if (printer == null && parser == null) {
0215:                    throw new IllegalArgumentException(
0216:                            "No printer or parser supplied");
0217:                }
0218:                clearPrefix();
0219:                append0(printer, parser);
0220:                return this ;
0221:            }
0222:
0223:            /**
0224:             * Instructs the printer to emit specific text, and the parser to expect it.
0225:             * The parser is case-insensitive.
0226:             *
0227:             * @return this PeriodFormatterBuilder
0228:             * @throws IllegalArgumentException if text is null
0229:             */
0230:            public PeriodFormatterBuilder appendLiteral(String text) {
0231:                if (text == null) {
0232:                    throw new IllegalArgumentException(
0233:                            "Literal must not be null");
0234:                }
0235:                clearPrefix();
0236:                Literal literal = new Literal(text);
0237:                append0(literal, literal);
0238:                return this ;
0239:            }
0240:
0241:            /**
0242:             * Set the minimum digits printed for the next and following appended
0243:             * fields. By default, the minimum digits printed is one. If the field value
0244:             * is zero, it is not printed unless a printZero rule is applied.
0245:             *
0246:             * @return this PeriodFormatterBuilder
0247:             */
0248:            public PeriodFormatterBuilder minimumPrintedDigits(int minDigits) {
0249:                iMinPrintedDigits = minDigits;
0250:                return this ;
0251:            }
0252:
0253:            /**
0254:             * Set the maximum digits parsed for the next and following appended
0255:             * fields. By default, the maximum digits parsed is ten.
0256:             *
0257:             * @return this PeriodFormatterBuilder
0258:             */
0259:            public PeriodFormatterBuilder maximumParsedDigits(int maxDigits) {
0260:                iMaxParsedDigits = maxDigits;
0261:                return this ;
0262:            }
0263:
0264:            /**
0265:             * Reject signed values when parsing the next and following appended fields.
0266:             *
0267:             * @return this PeriodFormatterBuilder
0268:             */
0269:            public PeriodFormatterBuilder rejectSignedValues(boolean v) {
0270:                iRejectSignedValues = v;
0271:                return this ;
0272:            }
0273:
0274:            /**
0275:             * Never print zero values for the next and following appended fields,
0276:             * unless no fields would be printed. If no fields are printed, the printer
0277:             * forces the last "printZeroRarely" field to print a zero.
0278:             * <p>
0279:             * This field setting is the default.
0280:             *
0281:             * @return this PeriodFormatterBuilder
0282:             */
0283:            public PeriodFormatterBuilder printZeroRarelyLast() {
0284:                iPrintZeroSetting = PRINT_ZERO_RARELY_LAST;
0285:                return this ;
0286:            }
0287:
0288:            /**
0289:             * Never print zero values for the next and following appended fields,
0290:             * unless no fields would be printed. If no fields are printed, the printer
0291:             * forces the first "printZeroRarely" field to print a zero.
0292:             *
0293:             * @return this PeriodFormatterBuilder
0294:             */
0295:            public PeriodFormatterBuilder printZeroRarelyFirst() {
0296:                iPrintZeroSetting = PRINT_ZERO_RARELY_FIRST;
0297:                return this ;
0298:            }
0299:
0300:            /**
0301:             * Print zero values for the next and following appened fields only if the
0302:             * period supports it.
0303:             *
0304:             * @return this PeriodFormatterBuilder
0305:             */
0306:            public PeriodFormatterBuilder printZeroIfSupported() {
0307:                iPrintZeroSetting = PRINT_ZERO_IF_SUPPORTED;
0308:                return this ;
0309:            }
0310:
0311:            /**
0312:             * Always print zero values for the next and following appended fields,
0313:             * even if the period doesn't support it. The parser requires values for
0314:             * fields that always print zero.
0315:             *
0316:             * @return this PeriodFormatterBuilder
0317:             */
0318:            public PeriodFormatterBuilder printZeroAlways() {
0319:                iPrintZeroSetting = PRINT_ZERO_ALWAYS;
0320:                return this ;
0321:            }
0322:
0323:            /**
0324:             * Never print zero values for the next and following appended fields,
0325:             * unless no fields would be printed. If no fields are printed, the printer
0326:             * forces the last "printZeroRarely" field to print a zero.
0327:             * <p>
0328:             * This field setting is the default.
0329:             *
0330:             * @return this PeriodFormatterBuilder
0331:             */
0332:            public PeriodFormatterBuilder printZeroNever() {
0333:                iPrintZeroSetting = PRINT_ZERO_NEVER;
0334:                return this ;
0335:            }
0336:
0337:            //-----------------------------------------------------------------------
0338:            /**
0339:             * Append a field prefix which applies only to the next appended field. If
0340:             * the field is not printed, neither is the prefix.
0341:             *
0342:             * @param text text to print before field only if field is printed
0343:             * @return this PeriodFormatterBuilder
0344:             * @see #appendSuffix
0345:             */
0346:            public PeriodFormatterBuilder appendPrefix(String text) {
0347:                if (text == null) {
0348:                    throw new IllegalArgumentException();
0349:                }
0350:                return appendPrefix(new SimpleAffix(text));
0351:            }
0352:
0353:            /**
0354:             * Append a field prefix which applies only to the next appended field. If
0355:             * the field is not printed, neither is the prefix.
0356:             * <p>
0357:             * During parsing, the singular and plural versions are accepted whether
0358:             * or not the actual value matches plurality.
0359:             *
0360:             * @param singularText text to print if field value is one
0361:             * @param pluralText text to print if field value is not one
0362:             * @return this PeriodFormatterBuilder
0363:             * @see #appendSuffix
0364:             */
0365:            public PeriodFormatterBuilder appendPrefix(String singularText,
0366:                    String pluralText) {
0367:                if (singularText == null || pluralText == null) {
0368:                    throw new IllegalArgumentException();
0369:                }
0370:                return appendPrefix(new PluralAffix(singularText, pluralText));
0371:            }
0372:
0373:            /**
0374:             * Append a field prefix which applies only to the next appended field. If
0375:             * the field is not printed, neither is the prefix.
0376:             *
0377:             * @param prefix custom prefix
0378:             * @return this PeriodFormatterBuilder
0379:             * @see #appendSuffix
0380:             */
0381:            private PeriodFormatterBuilder appendPrefix(PeriodFieldAffix prefix) {
0382:                if (prefix == null) {
0383:                    throw new IllegalArgumentException();
0384:                }
0385:                if (iPrefix != null) {
0386:                    prefix = new CompositeAffix(iPrefix, prefix);
0387:                }
0388:                iPrefix = prefix;
0389:                return this ;
0390:            }
0391:
0392:            //-----------------------------------------------------------------------
0393:            /**
0394:             * Instruct the printer to emit an integer years field, if supported.
0395:             * <p>
0396:             * The number of printed and parsed digits can be controlled using
0397:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0398:             *
0399:             * @return this PeriodFormatterBuilder
0400:             */
0401:            public PeriodFormatterBuilder appendYears() {
0402:                appendField(YEARS);
0403:                return this ;
0404:            }
0405:
0406:            /**
0407:             * Instruct the printer to emit an integer months field, if supported.
0408:             * <p>
0409:             * The number of printed and parsed digits can be controlled using
0410:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0411:             *
0412:             * @return this PeriodFormatterBuilder
0413:             */
0414:            public PeriodFormatterBuilder appendMonths() {
0415:                appendField(MONTHS);
0416:                return this ;
0417:            }
0418:
0419:            /**
0420:             * Instruct the printer to emit an integer weeks field, if supported.
0421:             * <p>
0422:             * The number of printed and parsed digits can be controlled using
0423:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0424:             *
0425:             * @return this PeriodFormatterBuilder
0426:             */
0427:            public PeriodFormatterBuilder appendWeeks() {
0428:                appendField(WEEKS);
0429:                return this ;
0430:            }
0431:
0432:            /**
0433:             * Instruct the printer to emit an integer days field, if supported.
0434:             * <p>
0435:             * The number of printed and parsed digits can be controlled using
0436:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0437:             *
0438:             * @return this PeriodFormatterBuilder
0439:             */
0440:            public PeriodFormatterBuilder appendDays() {
0441:                appendField(DAYS);
0442:                return this ;
0443:            }
0444:
0445:            /**
0446:             * Instruct the printer to emit an integer hours field, if supported.
0447:             * <p>
0448:             * The number of printed and parsed digits can be controlled using
0449:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0450:             *
0451:             * @return this PeriodFormatterBuilder
0452:             */
0453:            public PeriodFormatterBuilder appendHours() {
0454:                appendField(HOURS);
0455:                return this ;
0456:            }
0457:
0458:            /**
0459:             * Instruct the printer to emit an integer minutes field, if supported.
0460:             * <p>
0461:             * The number of printed and parsed digits can be controlled using
0462:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0463:             *
0464:             * @return this PeriodFormatterBuilder
0465:             */
0466:            public PeriodFormatterBuilder appendMinutes() {
0467:                appendField(MINUTES);
0468:                return this ;
0469:            }
0470:
0471:            /**
0472:             * Instruct the printer to emit an integer seconds field, if supported.
0473:             * <p>
0474:             * The number of printed and parsed digits can be controlled using
0475:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0476:             *
0477:             * @return this PeriodFormatterBuilder
0478:             */
0479:            public PeriodFormatterBuilder appendSeconds() {
0480:                appendField(SECONDS);
0481:                return this ;
0482:            }
0483:
0484:            /**
0485:             * Instruct the printer to emit a combined seconds and millis field, if supported.
0486:             * The millis will overflow into the seconds if necessary.
0487:             * The millis are always output.
0488:             *
0489:             * @return this PeriodFormatterBuilder
0490:             */
0491:            public PeriodFormatterBuilder appendSecondsWithMillis() {
0492:                appendField(SECONDS_MILLIS);
0493:                return this ;
0494:            }
0495:
0496:            /**
0497:             * Instruct the printer to emit a combined seconds and millis field, if supported.
0498:             * The millis will overflow into the seconds if necessary.
0499:             * The millis are only output if non-zero.
0500:             *
0501:             * @return this PeriodFormatterBuilder
0502:             */
0503:            public PeriodFormatterBuilder appendSecondsWithOptionalMillis() {
0504:                appendField(SECONDS_OPTIONAL_MILLIS);
0505:                return this ;
0506:            }
0507:
0508:            /**
0509:             * Instruct the printer to emit an integer millis field, if supported.
0510:             * <p>
0511:             * The number of printed and parsed digits can be controlled using
0512:             * {@link #minimumPrintedDigits(int)} and {@link #maximumParsedDigits(int)}.
0513:             *
0514:             * @return this PeriodFormatterBuilder
0515:             */
0516:            public PeriodFormatterBuilder appendMillis() {
0517:                appendField(MILLIS);
0518:                return this ;
0519:            }
0520:
0521:            /**
0522:             * Instruct the printer to emit an integer millis field, if supported.
0523:             * <p>
0524:             * The number of arsed digits can be controlled using {@link #maximumParsedDigits(int)}.
0525:             *
0526:             * @return this PeriodFormatterBuilder
0527:             */
0528:            public PeriodFormatterBuilder appendMillis3Digit() {
0529:                appendField(7, 3);
0530:                return this ;
0531:            }
0532:
0533:            private void appendField(int type) {
0534:                appendField(type, iMinPrintedDigits);
0535:            }
0536:
0537:            private void appendField(int type, int minPrinted) {
0538:                FieldFormatter field = new FieldFormatter(minPrinted,
0539:                        iPrintZeroSetting, iMaxParsedDigits,
0540:                        iRejectSignedValues, type, iFieldFormatters, iPrefix,
0541:                        null);
0542:                append0(field, field);
0543:                iFieldFormatters[type] = field;
0544:                iPrefix = null;
0545:            }
0546:
0547:            //-----------------------------------------------------------------------
0548:            /**
0549:             * Append a field suffix which applies only to the last appended field. If
0550:             * the field is not printed, neither is the suffix.
0551:             *
0552:             * @param text text to print after field only if field is printed
0553:             * @return this PeriodFormatterBuilder
0554:             * @throws IllegalStateException if no field exists to append to
0555:             * @see #appendPrefix
0556:             */
0557:            public PeriodFormatterBuilder appendSuffix(String text) {
0558:                if (text == null) {
0559:                    throw new IllegalArgumentException();
0560:                }
0561:                return appendSuffix(new SimpleAffix(text));
0562:            }
0563:
0564:            /**
0565:             * Append a field suffix which applies only to the last appended field. If
0566:             * the field is not printed, neither is the suffix.
0567:             * <p>
0568:             * During parsing, the singular and plural versions are accepted whether or
0569:             * not the actual value matches plurality.
0570:             *
0571:             * @param singularText text to print if field value is one
0572:             * @param pluralText text to print if field value is not one
0573:             * @return this PeriodFormatterBuilder
0574:             * @throws IllegalStateException if no field exists to append to
0575:             * @see #appendPrefix
0576:             */
0577:            public PeriodFormatterBuilder appendSuffix(String singularText,
0578:                    String pluralText) {
0579:                if (singularText == null || pluralText == null) {
0580:                    throw new IllegalArgumentException();
0581:                }
0582:                return appendSuffix(new PluralAffix(singularText, pluralText));
0583:            }
0584:
0585:            /**
0586:             * Append a field suffix which applies only to the last appended field. If
0587:             * the field is not printed, neither is the suffix.
0588:             *
0589:             * @param suffix custom suffix
0590:             * @return this PeriodFormatterBuilder
0591:             * @throws IllegalStateException if no field exists to append to
0592:             * @see #appendPrefix
0593:             */
0594:            private PeriodFormatterBuilder appendSuffix(PeriodFieldAffix suffix) {
0595:                final Object originalPrinter;
0596:                final Object originalParser;
0597:                if (iElementPairs.size() > 0) {
0598:                    originalPrinter = iElementPairs
0599:                            .get(iElementPairs.size() - 2);
0600:                    originalParser = iElementPairs
0601:                            .get(iElementPairs.size() - 1);
0602:                } else {
0603:                    originalPrinter = null;
0604:                    originalParser = null;
0605:                }
0606:
0607:                if (originalPrinter == null || originalParser == null
0608:                        || originalPrinter != originalParser
0609:                        || !(originalPrinter instanceof  FieldFormatter)) {
0610:                    throw new IllegalStateException(
0611:                            "No field to apply suffix to");
0612:                }
0613:
0614:                clearPrefix();
0615:                FieldFormatter newField = new FieldFormatter(
0616:                        (FieldFormatter) originalPrinter, suffix);
0617:                iElementPairs.set(iElementPairs.size() - 2, newField);
0618:                iElementPairs.set(iElementPairs.size() - 1, newField);
0619:                iFieldFormatters[newField.getFieldType()] = newField;
0620:
0621:                return this ;
0622:            }
0623:
0624:            //-----------------------------------------------------------------------
0625:            /**
0626:             * Append a separator, which is output if fields are printed both before
0627:             * and after the separator.
0628:             * <p>
0629:             * For example, <code>builder.appendDays().appendSeparator(",").appendHours()</code>
0630:             * will only output the comma if both the days and hours fields are output.
0631:             * <p>
0632:             * The text will be parsed case-insensitively.
0633:             * <p>
0634:             * Note: appending a separator discontinues any further work on the latest
0635:             * appended field.
0636:             *
0637:             * @param text  the text to use as a separator
0638:             * @return this PeriodFormatterBuilder
0639:             * @throws IllegalStateException if this separator follows a previous one
0640:             */
0641:            public PeriodFormatterBuilder appendSeparator(String text) {
0642:                return appendSeparator(text, text, null, true, true);
0643:            }
0644:
0645:            /**
0646:             * Append a separator, which is output only if fields are printed after the separator.
0647:             * <p>
0648:             * For example,
0649:             * <code>builder.appendDays().appendSeparatorIfFieldsAfter(",").appendHours()</code>
0650:             * will only output the comma if the hours fields is output.
0651:             * <p>
0652:             * The text will be parsed case-insensitively.
0653:             * <p>
0654:             * Note: appending a separator discontinues any further work on the latest
0655:             * appended field.
0656:             *
0657:             * @param text  the text to use as a separator
0658:             * @return this PeriodFormatterBuilder
0659:             * @throws IllegalStateException if this separator follows a previous one
0660:             */
0661:            public PeriodFormatterBuilder appendSeparatorIfFieldsAfter(
0662:                    String text) {
0663:                return appendSeparator(text, text, null, false, true);
0664:            }
0665:
0666:            /**
0667:             * Append a separator, which is output only if fields are printed before the separator.
0668:             * <p>
0669:             * For example,
0670:             * <code>builder.appendDays().appendSeparatorIfFieldsBefore(",").appendHours()</code>
0671:             * will only output the comma if the days fields is output.
0672:             * <p>
0673:             * The text will be parsed case-insensitively.
0674:             * <p>
0675:             * Note: appending a separator discontinues any further work on the latest
0676:             * appended field.
0677:             *
0678:             * @param text  the text to use as a separator
0679:             * @return this PeriodFormatterBuilder
0680:             * @throws IllegalStateException if this separator follows a previous one
0681:             */
0682:            public PeriodFormatterBuilder appendSeparatorIfFieldsBefore(
0683:                    String text) {
0684:                return appendSeparator(text, text, null, true, false);
0685:            }
0686:
0687:            /**
0688:             * Append a separator, which is output if fields are printed both before
0689:             * and after the separator.
0690:             * <p>
0691:             * This method changes the separator depending on whether it is the last separator
0692:             * to be output.
0693:             * <p>
0694:             * For example, <code>builder.appendDays().appendSeparator(",", "&").appendHours().appendSeparator(",", "&").appendMinutes()</code>
0695:             * will output '1,2&3' if all three fields are output, '1&2' if two fields are output
0696:             * and '1' if just one field is output.
0697:             * <p>
0698:             * The text will be parsed case-insensitively.
0699:             * <p>
0700:             * Note: appending a separator discontinues any further work on the latest
0701:             * appended field.
0702:             *
0703:             * @param text  the text to use as a separator
0704:             * @param finalText  the text used used if this is the final separator to be printed
0705:             * @return this PeriodFormatterBuilder
0706:             * @throws IllegalStateException if this separator follows a previous one
0707:             */
0708:            public PeriodFormatterBuilder appendSeparator(String text,
0709:                    String finalText) {
0710:                return appendSeparator(text, finalText, null, true, true);
0711:            }
0712:
0713:            /**
0714:             * Append a separator, which is output if fields are printed both before
0715:             * and after the separator.
0716:             * <p>
0717:             * This method changes the separator depending on whether it is the last separator
0718:             * to be output.
0719:             * <p>
0720:             * For example, <code>builder.appendDays().appendSeparator(",", "&").appendHours().appendSeparator(",", "&").appendMinutes()</code>
0721:             * will output '1,2&3' if all three fields are output, '1&2' if two fields are output
0722:             * and '1' if just one field is output.
0723:             * <p>
0724:             * The text will be parsed case-insensitively.
0725:             * <p>
0726:             * Note: appending a separator discontinues any further work on the latest
0727:             * appended field.
0728:             *
0729:             * @param text  the text to use as a separator
0730:             * @param finalText  the text used used if this is the final separator to be printed
0731:             * @param variants  set of text values which are also acceptable when parsed
0732:             * @return this PeriodFormatterBuilder
0733:             * @throws IllegalStateException if this separator follows a previous one
0734:             */
0735:            public PeriodFormatterBuilder appendSeparator(String text,
0736:                    String finalText, String[] variants) {
0737:                return appendSeparator(text, finalText, variants, true, true);
0738:            }
0739:
0740:            private PeriodFormatterBuilder appendSeparator(String text,
0741:                    String finalText, String[] variants, boolean useBefore,
0742:                    boolean useAfter) {
0743:                if (text == null || finalText == null) {
0744:                    throw new IllegalArgumentException();
0745:                }
0746:
0747:                clearPrefix();
0748:
0749:                // optimise zero formatter case
0750:                List pairs = iElementPairs;
0751:                if (pairs.size() == 0) {
0752:                    if (useAfter && useBefore == false) {
0753:                        Separator separator = new Separator(text, finalText,
0754:                                variants, Literal.EMPTY, Literal.EMPTY,
0755:                                useBefore, useAfter);
0756:                        append0(separator, separator);
0757:                    }
0758:                    return this ;
0759:                }
0760:
0761:                // find the last separator added
0762:                int i;
0763:                Separator lastSeparator = null;
0764:                for (i = pairs.size(); --i >= 0;) {
0765:                    if (pairs.get(i) instanceof  Separator) {
0766:                        lastSeparator = (Separator) pairs.get(i);
0767:                        pairs = pairs.subList(i + 1, pairs.size());
0768:                        break;
0769:                    }
0770:                    i--; // element pairs
0771:                }
0772:
0773:                // merge formatters
0774:                if (lastSeparator != null && pairs.size() == 0) {
0775:                    throw new IllegalStateException(
0776:                            "Cannot have two adjacent separators");
0777:                } else {
0778:                    Object[] comp = createComposite(pairs);
0779:                    pairs.clear();
0780:                    Separator separator = new Separator(text, finalText,
0781:                            variants, (PeriodPrinter) comp[0],
0782:                            (PeriodParser) comp[1], useBefore, useAfter);
0783:                    pairs.add(separator);
0784:                    pairs.add(separator);
0785:                }
0786:
0787:                return this ;
0788:            }
0789:
0790:            //-----------------------------------------------------------------------
0791:            private void clearPrefix() throws IllegalStateException {
0792:                if (iPrefix != null) {
0793:                    throw new IllegalStateException(
0794:                            "Prefix not followed by field");
0795:                }
0796:                iPrefix = null;
0797:            }
0798:
0799:            private PeriodFormatterBuilder append0(PeriodPrinter printer,
0800:                    PeriodParser parser) {
0801:                iElementPairs.add(printer);
0802:                iElementPairs.add(parser);
0803:                iNotPrinter |= (printer == null);
0804:                iNotParser |= (parser == null);
0805:                return this ;
0806:            }
0807:
0808:            //-----------------------------------------------------------------------
0809:            private static PeriodFormatter toFormatter(List elementPairs,
0810:                    boolean notPrinter, boolean notParser) {
0811:                if (notPrinter && notParser) {
0812:                    throw new IllegalStateException(
0813:                            "Builder has created neither a printer nor a parser");
0814:                }
0815:                int size = elementPairs.size();
0816:                if (size >= 2 && elementPairs.get(0) instanceof  Separator) {
0817:                    Separator sep = (Separator) elementPairs.get(0);
0818:                    PeriodFormatter f = toFormatter(elementPairs.subList(2,
0819:                            size), notPrinter, notParser);
0820:                    sep = sep.finish(f.getPrinter(), f.getParser());
0821:                    return new PeriodFormatter(sep, sep);
0822:                }
0823:                Object[] comp = createComposite(elementPairs);
0824:                if (notPrinter) {
0825:                    return new PeriodFormatter(null, (PeriodParser) comp[1]);
0826:                } else if (notParser) {
0827:                    return new PeriodFormatter((PeriodPrinter) comp[0], null);
0828:                } else {
0829:                    return new PeriodFormatter((PeriodPrinter) comp[0],
0830:                            (PeriodParser) comp[1]);
0831:                }
0832:            }
0833:
0834:            private static Object[] createComposite(List elementPairs) {
0835:                switch (elementPairs.size()) {
0836:                case 0:
0837:                    return new Object[] { Literal.EMPTY, Literal.EMPTY };
0838:                case 1:
0839:                    return new Object[] { elementPairs.get(0),
0840:                            elementPairs.get(1) };
0841:                default:
0842:                    Composite comp = new Composite(elementPairs);
0843:                    return new Object[] { comp, comp };
0844:                }
0845:            }
0846:
0847:            //-----------------------------------------------------------------------
0848:            /**
0849:             * Defines a formatted field's prefix or suffix text.
0850:             * This can be used for fields such as 'n hours' or 'nH' or 'Hour:n'.
0851:             */
0852:            static interface PeriodFieldAffix {
0853:                int calculatePrintedLength(int value);
0854:
0855:                void printTo(StringBuffer buf, int value);
0856:
0857:                void printTo(Writer out, int value) throws IOException;
0858:
0859:                /**
0860:                 * @return new position after parsing affix, or ~position of failure
0861:                 */
0862:                int parse(String periodStr, int position);
0863:
0864:                /**
0865:                 * @return position where affix starts, or original ~position if not found
0866:                 */
0867:                int scan(String periodStr, int position);
0868:            }
0869:
0870:            //-----------------------------------------------------------------------
0871:            /**
0872:             * Implements an affix where the text does not vary by the amount.
0873:             */
0874:            static class SimpleAffix implements  PeriodFieldAffix {
0875:                private final String iText;
0876:
0877:                SimpleAffix(String text) {
0878:                    iText = text;
0879:                }
0880:
0881:                public int calculatePrintedLength(int value) {
0882:                    return iText.length();
0883:                }
0884:
0885:                public void printTo(StringBuffer buf, int value) {
0886:                    buf.append(iText);
0887:                }
0888:
0889:                public void printTo(Writer out, int value) throws IOException {
0890:                    out.write(iText);
0891:                }
0892:
0893:                public int parse(String periodStr, int position) {
0894:                    String text = iText;
0895:                    int textLength = text.length();
0896:                    if (periodStr.regionMatches(true, position, text, 0,
0897:                            textLength)) {
0898:                        return position + textLength;
0899:                    }
0900:                    return ~position;
0901:                }
0902:
0903:                public int scan(String periodStr, final int position) {
0904:                    String text = iText;
0905:                    int textLength = text.length();
0906:                    int sourceLength = periodStr.length();
0907:                    search: for (int pos = position; pos < sourceLength; pos++) {
0908:                        if (periodStr.regionMatches(true, pos, text, 0,
0909:                                textLength)) {
0910:                            return pos;
0911:                        }
0912:                        // Only allow number characters to be skipped in search of suffix.
0913:                        switch (periodStr.charAt(pos)) {
0914:                        case '0':
0915:                        case '1':
0916:                        case '2':
0917:                        case '3':
0918:                        case '4':
0919:                        case '5':
0920:                        case '6':
0921:                        case '7':
0922:                        case '8':
0923:                        case '9':
0924:                        case '.':
0925:                        case ',':
0926:                        case '+':
0927:                        case '-':
0928:                            break;
0929:                        default:
0930:                            break search;
0931:                        }
0932:                    }
0933:                    return ~position;
0934:                }
0935:            }
0936:
0937:            //-----------------------------------------------------------------------
0938:            /**
0939:             * Implements an affix where the text varies by the amount of the field.
0940:             * Only singular (1) and plural (not 1) are supported.
0941:             */
0942:            static class PluralAffix implements  PeriodFieldAffix {
0943:                private final String iSingularText;
0944:                private final String iPluralText;
0945:
0946:                PluralAffix(String singularText, String pluralText) {
0947:                    iSingularText = singularText;
0948:                    iPluralText = pluralText;
0949:                }
0950:
0951:                public int calculatePrintedLength(int value) {
0952:                    return (value == 1 ? iSingularText : iPluralText).length();
0953:                }
0954:
0955:                public void printTo(StringBuffer buf, int value) {
0956:                    buf.append(value == 1 ? iSingularText : iPluralText);
0957:                }
0958:
0959:                public void printTo(Writer out, int value) throws IOException {
0960:                    out.write(value == 1 ? iSingularText : iPluralText);
0961:                }
0962:
0963:                public int parse(String periodStr, int position) {
0964:                    String text1 = iPluralText;
0965:                    String text2 = iSingularText;
0966:
0967:                    if (text1.length() < text2.length()) {
0968:                        // Swap in order to match longer one first.
0969:                        String temp = text1;
0970:                        text1 = text2;
0971:                        text2 = temp;
0972:                    }
0973:
0974:                    if (periodStr.regionMatches(true, position, text1, 0, text1
0975:                            .length())) {
0976:                        return position + text1.length();
0977:                    }
0978:                    if (periodStr.regionMatches(true, position, text2, 0, text2
0979:                            .length())) {
0980:                        return position + text2.length();
0981:                    }
0982:
0983:                    return ~position;
0984:                }
0985:
0986:                public int scan(String periodStr, final int position) {
0987:                    String text1 = iPluralText;
0988:                    String text2 = iSingularText;
0989:
0990:                    if (text1.length() < text2.length()) {
0991:                        // Swap in order to match longer one first.
0992:                        String temp = text1;
0993:                        text1 = text2;
0994:                        text2 = temp;
0995:                    }
0996:
0997:                    int textLength1 = text1.length();
0998:                    int textLength2 = text2.length();
0999:
1000:                    int sourceLength = periodStr.length();
1001:                    for (int pos = position; pos < sourceLength; pos++) {
1002:                        if (periodStr.regionMatches(true, pos, text1, 0,
1003:                                textLength1)) {
1004:                            return pos;
1005:                        }
1006:                        if (periodStr.regionMatches(true, pos, text2, 0,
1007:                                textLength2)) {
1008:                            return pos;
1009:                        }
1010:                    }
1011:                    return ~position;
1012:                }
1013:            }
1014:
1015:            //-----------------------------------------------------------------------
1016:            /**
1017:             * Builds a composite affix by merging two other affix implementations.
1018:             */
1019:            static class CompositeAffix implements  PeriodFieldAffix {
1020:                private final PeriodFieldAffix iLeft;
1021:                private final PeriodFieldAffix iRight;
1022:
1023:                CompositeAffix(PeriodFieldAffix left, PeriodFieldAffix right) {
1024:                    iLeft = left;
1025:                    iRight = right;
1026:                }
1027:
1028:                public int calculatePrintedLength(int value) {
1029:                    return iLeft.calculatePrintedLength(value)
1030:                            + iRight.calculatePrintedLength(value);
1031:                }
1032:
1033:                public void printTo(StringBuffer buf, int value) {
1034:                    iLeft.printTo(buf, value);
1035:                    iRight.printTo(buf, value);
1036:                }
1037:
1038:                public void printTo(Writer out, int value) throws IOException {
1039:                    iLeft.printTo(out, value);
1040:                    iRight.printTo(out, value);
1041:                }
1042:
1043:                public int parse(String periodStr, int position) {
1044:                    position = iLeft.parse(periodStr, position);
1045:                    if (position >= 0) {
1046:                        position = iRight.parse(periodStr, position);
1047:                    }
1048:                    return position;
1049:                }
1050:
1051:                public int scan(String periodStr, final int position) {
1052:                    int pos = iLeft.scan(periodStr, position);
1053:                    if (pos >= 0) {
1054:                        return iRight.scan(periodStr, pos);
1055:                    }
1056:                    return ~position;
1057:                }
1058:            }
1059:
1060:            //-----------------------------------------------------------------------
1061:            /**
1062:             * Formats the numeric value of a field, potentially with prefix/suffix.
1063:             */
1064:            static class FieldFormatter implements  PeriodPrinter, PeriodParser {
1065:                private final int iMinPrintedDigits;
1066:                private final int iPrintZeroSetting;
1067:                private final int iMaxParsedDigits;
1068:                private final boolean iRejectSignedValues;
1069:
1070:                /** The index of the field type, 0=year, etc. */
1071:                private final int iFieldType;
1072:                /**
1073:                 * The array of the latest formatter added for each type.
1074:                 * This is shared between all the field formatters in a formatter.
1075:                 */
1076:                private final FieldFormatter[] iFieldFormatters;
1077:
1078:                private final PeriodFieldAffix iPrefix;
1079:                private final PeriodFieldAffix iSuffix;
1080:
1081:                FieldFormatter(int minPrintedDigits, int printZeroSetting,
1082:                        int maxParsedDigits, boolean rejectSignedValues,
1083:                        int fieldType, FieldFormatter[] fieldFormatters,
1084:                        PeriodFieldAffix prefix, PeriodFieldAffix suffix) {
1085:                    iMinPrintedDigits = minPrintedDigits;
1086:                    iPrintZeroSetting = printZeroSetting;
1087:                    iMaxParsedDigits = maxParsedDigits;
1088:                    iRejectSignedValues = rejectSignedValues;
1089:                    iFieldType = fieldType;
1090:                    iFieldFormatters = fieldFormatters;
1091:                    iPrefix = prefix;
1092:                    iSuffix = suffix;
1093:                }
1094:
1095:                FieldFormatter(FieldFormatter field, PeriodFieldAffix suffix) {
1096:                    iMinPrintedDigits = field.iMinPrintedDigits;
1097:                    iPrintZeroSetting = field.iPrintZeroSetting;
1098:                    iMaxParsedDigits = field.iMaxParsedDigits;
1099:                    iRejectSignedValues = field.iRejectSignedValues;
1100:                    iFieldType = field.iFieldType;
1101:                    iFieldFormatters = field.iFieldFormatters;
1102:                    iPrefix = field.iPrefix;
1103:                    if (field.iSuffix != null) {
1104:                        suffix = new CompositeAffix(field.iSuffix, suffix);
1105:                    }
1106:                    iSuffix = suffix;
1107:                }
1108:
1109:                public int countFieldsToPrint(ReadablePeriod period,
1110:                        int stopAt, Locale locale) {
1111:                    if (stopAt <= 0) {
1112:                        return 0;
1113:                    }
1114:                    if (iPrintZeroSetting == PRINT_ZERO_ALWAYS
1115:                            || getFieldValue(period) != Long.MAX_VALUE) {
1116:                        return 1;
1117:                    }
1118:                    return 0;
1119:                }
1120:
1121:                public int calculatePrintedLength(ReadablePeriod period,
1122:                        Locale locale) {
1123:                    long valueLong = getFieldValue(period);
1124:                    if (valueLong == Long.MAX_VALUE) {
1125:                        return 0;
1126:                    }
1127:
1128:                    int sum = Math.max(FormatUtils
1129:                            .calculateDigitCount(valueLong), iMinPrintedDigits);
1130:                    if (iFieldType >= SECONDS_MILLIS) {
1131:                        // valueLong contains the seconds and millis fields
1132:                        // the minimum output is 0.000, which is 4 digits
1133:                        sum = Math.max(sum, 4);
1134:                        // plus one for the decimal point
1135:                        sum++;
1136:                        if (iFieldType == SECONDS_OPTIONAL_MILLIS
1137:                                && (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND) == 0) {
1138:                            sum -= 4; // remove three digits and decimal point
1139:                        }
1140:                        // reset valueLong to refer to the seconds part for the prefic/suffix calculation
1141:                        valueLong = valueLong
1142:                                / DateTimeConstants.MILLIS_PER_SECOND;
1143:                    }
1144:                    int value = (int) valueLong;
1145:
1146:                    if (iPrefix != null) {
1147:                        sum += iPrefix.calculatePrintedLength(value);
1148:                    }
1149:                    if (iSuffix != null) {
1150:                        sum += iSuffix.calculatePrintedLength(value);
1151:                    }
1152:
1153:                    return sum;
1154:                }
1155:
1156:                public void printTo(StringBuffer buf, ReadablePeriod period,
1157:                        Locale locale) {
1158:                    long valueLong = getFieldValue(period);
1159:                    if (valueLong == Long.MAX_VALUE) {
1160:                        return;
1161:                    }
1162:                    int value = (int) valueLong;
1163:                    if (iFieldType >= SECONDS_MILLIS) {
1164:                        value = (int) (valueLong / DateTimeConstants.MILLIS_PER_SECOND);
1165:                    }
1166:
1167:                    if (iPrefix != null) {
1168:                        iPrefix.printTo(buf, value);
1169:                    }
1170:                    int minDigits = iMinPrintedDigits;
1171:                    if (minDigits <= 1) {
1172:                        FormatUtils.appendUnpaddedInteger(buf, value);
1173:                    } else {
1174:                        FormatUtils.appendPaddedInteger(buf, value, minDigits);
1175:                    }
1176:                    if (iFieldType >= SECONDS_MILLIS) {
1177:                        int dp = (int) (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND);
1178:                        if (iFieldType == SECONDS_MILLIS || dp > 0) {
1179:                            buf.append('.');
1180:                            FormatUtils.appendPaddedInteger(buf, dp, 3);
1181:                        }
1182:                    }
1183:                    if (iSuffix != null) {
1184:                        iSuffix.printTo(buf, value);
1185:                    }
1186:                }
1187:
1188:                public void printTo(Writer out, ReadablePeriod period,
1189:                        Locale locale) throws IOException {
1190:                    long valueLong = getFieldValue(period);
1191:                    if (valueLong == Long.MAX_VALUE) {
1192:                        return;
1193:                    }
1194:                    int value = (int) valueLong;
1195:                    if (iFieldType >= SECONDS_MILLIS) {
1196:                        value = (int) (valueLong / DateTimeConstants.MILLIS_PER_SECOND);
1197:                    }
1198:
1199:                    if (iPrefix != null) {
1200:                        iPrefix.printTo(out, value);
1201:                    }
1202:                    int minDigits = iMinPrintedDigits;
1203:                    if (minDigits <= 1) {
1204:                        FormatUtils.writeUnpaddedInteger(out, value);
1205:                    } else {
1206:                        FormatUtils.writePaddedInteger(out, value, minDigits);
1207:                    }
1208:                    if (iFieldType >= SECONDS_MILLIS) {
1209:                        int dp = (int) (Math.abs(valueLong) % DateTimeConstants.MILLIS_PER_SECOND);
1210:                        if (iFieldType == SECONDS_MILLIS || dp > 0) {
1211:                            out.write('.');
1212:                            FormatUtils.writePaddedInteger(out, dp, 3);
1213:                        }
1214:                    }
1215:                    if (iSuffix != null) {
1216:                        iSuffix.printTo(out, value);
1217:                    }
1218:                }
1219:
1220:                public int parseInto(ReadWritablePeriod period, String text,
1221:                        int position, Locale locale) {
1222:
1223:                    boolean mustParse = (iPrintZeroSetting == PRINT_ZERO_ALWAYS);
1224:
1225:                    // Shortcut test.
1226:                    if (position >= text.length()) {
1227:                        return mustParse ? ~position : position;
1228:                    }
1229:
1230:                    if (iPrefix != null) {
1231:                        position = iPrefix.parse(text, position);
1232:                        if (position >= 0) {
1233:                            // If prefix is found, then the parse must finish.
1234:                            mustParse = true;
1235:                        } else {
1236:                            // Prefix not found, so bail.
1237:                            if (!mustParse) {
1238:                                // It's okay because parsing of this field is not
1239:                                // required. Don't return an error. Fields down the
1240:                                // chain can continue on, trying to parse.
1241:                                return ~position;
1242:                            }
1243:                            return position;
1244:                        }
1245:                    }
1246:
1247:                    int suffixPos = -1;
1248:                    if (iSuffix != null && !mustParse) {
1249:                        // Pre-scan the suffix, to help determine if this field must be
1250:                        // parsed.
1251:                        suffixPos = iSuffix.scan(text, position);
1252:                        if (suffixPos >= 0) {
1253:                            // If suffix is found, then parse must finish.
1254:                            mustParse = true;
1255:                        } else {
1256:                            // Suffix not found, so bail.
1257:                            if (!mustParse) {
1258:                                // It's okay because parsing of this field is not
1259:                                // required. Don't return an error. Fields down the
1260:                                // chain can continue on, trying to parse.
1261:                                return ~suffixPos;
1262:                            }
1263:                            return suffixPos;
1264:                        }
1265:                    }
1266:
1267:                    if (!mustParse
1268:                            && !isSupported(period.getPeriodType(), iFieldType)) {
1269:                        // If parsing is not required and the field is not supported,
1270:                        // exit gracefully so that another parser can continue on.
1271:                        return position;
1272:                    }
1273:
1274:                    int limit;
1275:                    if (suffixPos > 0) {
1276:                        limit = Math
1277:                                .min(iMaxParsedDigits, suffixPos - position);
1278:                    } else {
1279:                        limit = Math.min(iMaxParsedDigits, text.length()
1280:                                - position);
1281:                    }
1282:
1283:                    // validate input number
1284:                    int length = 0;
1285:                    int fractPos = -1;
1286:                    boolean hasDigits = false;
1287:                    while (length < limit) {
1288:                        char c = text.charAt(position + length);
1289:                        // leading sign
1290:                        if (length == 0 && (c == '-' || c == '+')
1291:                                && !iRejectSignedValues) {
1292:                            boolean negative = c == '-';
1293:
1294:                            // Next character must be a digit.
1295:                            if (length + 1 >= limit
1296:                                    || (c = text.charAt(position + length + 1)) < '0'
1297:                                    || c > '9') {
1298:                                break;
1299:                            }
1300:
1301:                            if (negative) {
1302:                                length++;
1303:                            } else {
1304:                                // Skip the '+' for parseInt to succeed.
1305:                                position++;
1306:                            }
1307:                            // Expand the limit to disregard the sign character.
1308:                            limit = Math.min(limit + 1, text.length()
1309:                                    - position);
1310:                            continue;
1311:                        }
1312:                        // main number
1313:                        if (c >= '0' && c <= '9') {
1314:                            hasDigits = true;
1315:                        } else {
1316:                            if ((c == '.' || c == ',')
1317:                                    && (iFieldType == SECONDS_MILLIS || iFieldType == SECONDS_OPTIONAL_MILLIS)) {
1318:                                if (fractPos >= 0) {
1319:                                    // can't have two decimals
1320:                                    break;
1321:                                }
1322:                                fractPos = position + length + 1;
1323:                                // Expand the limit to disregard the decimal point.
1324:                                limit = Math.min(limit + 1, text.length()
1325:                                        - position);
1326:                            } else {
1327:                                break;
1328:                            }
1329:                        }
1330:                        length++;
1331:                    }
1332:
1333:                    if (!hasDigits) {
1334:                        return ~position;
1335:                    }
1336:
1337:                    if (suffixPos >= 0 && position + length != suffixPos) {
1338:                        // If there are additional non-digit characters before the
1339:                        // suffix is reached, then assume that the suffix found belongs
1340:                        // to a field not yet reached. Return original position so that
1341:                        // another parser can continue on.
1342:                        return position;
1343:                    }
1344:
1345:                    if (iFieldType != SECONDS_MILLIS
1346:                            && iFieldType != SECONDS_OPTIONAL_MILLIS) {
1347:                        // Handle common case.
1348:                        setFieldValue(period, iFieldType, parseInt(text,
1349:                                position, length));
1350:                    } else if (fractPos < 0) {
1351:                        setFieldValue(period, SECONDS, parseInt(text, position,
1352:                                length));
1353:                        setFieldValue(period, MILLIS, 0);
1354:                    } else {
1355:                        int wholeValue = parseInt(text, position, fractPos
1356:                                - position - 1);
1357:                        setFieldValue(period, SECONDS, wholeValue);
1358:
1359:                        int fractLen = position + length - fractPos;
1360:                        int fractValue;
1361:                        if (fractLen <= 0) {
1362:                            fractValue = 0;
1363:                        } else {
1364:                            if (fractLen >= 3) {
1365:                                fractValue = parseInt(text, fractPos, 3);
1366:                            } else {
1367:                                fractValue = parseInt(text, fractPos, fractLen);
1368:                                if (fractLen == 1) {
1369:                                    fractValue *= 100;
1370:                                } else {
1371:                                    fractValue *= 10;
1372:                                }
1373:                            }
1374:                            if (wholeValue < 0) {
1375:                                fractValue = -fractValue;
1376:                            }
1377:                        }
1378:
1379:                        setFieldValue(period, MILLIS, fractValue);
1380:                    }
1381:
1382:                    position += length;
1383:
1384:                    if (position >= 0 && iSuffix != null) {
1385:                        position = iSuffix.parse(text, position);
1386:                    }
1387:
1388:                    return position;
1389:                }
1390:
1391:                /**
1392:                 * @param text text to parse
1393:                 * @param position position in text
1394:                 * @param length exact count of characters to parse
1395:                 * @return parsed int value
1396:                 */
1397:                private int parseInt(String text, int position, int length) {
1398:                    if (length >= 10) {
1399:                        // Since value may exceed max, use stock parser which checks for this.
1400:                        return Integer.parseInt(text.substring(position,
1401:                                position + length));
1402:                    }
1403:                    if (length <= 0) {
1404:                        return 0;
1405:                    }
1406:                    int value = text.charAt(position++);
1407:                    length--;
1408:                    boolean negative;
1409:                    if (value == '-') {
1410:                        if (--length < 0) {
1411:                            return 0;
1412:                        }
1413:                        negative = true;
1414:                        value = text.charAt(position++);
1415:                    } else {
1416:                        negative = false;
1417:                    }
1418:                    value -= '0';
1419:                    while (length-- > 0) {
1420:                        value = ((value << 3) + (value << 1))
1421:                                + text.charAt(position++) - '0';
1422:                    }
1423:                    return negative ? -value : value;
1424:                }
1425:
1426:                /**
1427:                 * @return Long.MAX_VALUE if nothing to print, otherwise value
1428:                 */
1429:                long getFieldValue(ReadablePeriod period) {
1430:                    PeriodType type;
1431:                    if (iPrintZeroSetting == PRINT_ZERO_ALWAYS) {
1432:                        type = null; // Don't need to check if supported.
1433:                    } else {
1434:                        type = period.getPeriodType();
1435:                    }
1436:                    if (type != null && isSupported(type, iFieldType) == false) {
1437:                        return Long.MAX_VALUE;
1438:                    }
1439:
1440:                    long value;
1441:
1442:                    switch (iFieldType) {
1443:                    default:
1444:                        return Long.MAX_VALUE;
1445:                    case YEARS:
1446:                        value = period.get(DurationFieldType.years());
1447:                        break;
1448:                    case MONTHS:
1449:                        value = period.get(DurationFieldType.months());
1450:                        break;
1451:                    case WEEKS:
1452:                        value = period.get(DurationFieldType.weeks());
1453:                        break;
1454:                    case DAYS:
1455:                        value = period.get(DurationFieldType.days());
1456:                        break;
1457:                    case HOURS:
1458:                        value = period.get(DurationFieldType.hours());
1459:                        break;
1460:                    case MINUTES:
1461:                        value = period.get(DurationFieldType.minutes());
1462:                        break;
1463:                    case SECONDS:
1464:                        value = period.get(DurationFieldType.seconds());
1465:                        break;
1466:                    case MILLIS:
1467:                        value = period.get(DurationFieldType.millis());
1468:                        break;
1469:                    case SECONDS_MILLIS: // drop through
1470:                    case SECONDS_OPTIONAL_MILLIS:
1471:                        int seconds = period.get(DurationFieldType.seconds());
1472:                        int millis = period.get(DurationFieldType.millis());
1473:                        value = (seconds * (long) DateTimeConstants.MILLIS_PER_SECOND)
1474:                                + millis;
1475:                        break;
1476:                    }
1477:
1478:                    // determine if period is zero and this is the last field
1479:                    if (value == 0) {
1480:                        switch (iPrintZeroSetting) {
1481:                        case PRINT_ZERO_NEVER:
1482:                            return Long.MAX_VALUE;
1483:                        case PRINT_ZERO_RARELY_LAST:
1484:                            if (isZero(period)
1485:                                    && iFieldFormatters[iFieldType] == this ) {
1486:                                for (int i = iFieldType + 1; i <= MAX_FIELD; i++) {
1487:                                    if (isSupported(type, i)
1488:                                            && iFieldFormatters[i] != null) {
1489:                                        return Long.MAX_VALUE;
1490:                                    }
1491:                                }
1492:                            } else {
1493:                                return Long.MAX_VALUE;
1494:                            }
1495:                            break;
1496:                        case PRINT_ZERO_RARELY_FIRST:
1497:                            if (isZero(period)
1498:                                    && iFieldFormatters[iFieldType] == this ) {
1499:                                int i = Math.min(iFieldType, 8); // line split out for IBM JDK
1500:                                i--; // see bug 1660490
1501:                                for (; i >= 0 && i <= MAX_FIELD; i++) {
1502:                                    if (isSupported(type, i)
1503:                                            && iFieldFormatters[i] != null) {
1504:                                        return Long.MAX_VALUE;
1505:                                    }
1506:                                }
1507:                            } else {
1508:                                return Long.MAX_VALUE;
1509:                            }
1510:                            break;
1511:                        }
1512:                    }
1513:
1514:                    return value;
1515:                }
1516:
1517:                boolean isZero(ReadablePeriod period) {
1518:                    for (int i = 0, isize = period.size(); i < isize; i++) {
1519:                        if (period.getValue(i) != 0) {
1520:                            return false;
1521:                        }
1522:                    }
1523:                    return true;
1524:                }
1525:
1526:                boolean isSupported(PeriodType type, int field) {
1527:                    switch (field) {
1528:                    default:
1529:                        return false;
1530:                    case YEARS:
1531:                        return type.isSupported(DurationFieldType.years());
1532:                    case MONTHS:
1533:                        return type.isSupported(DurationFieldType.months());
1534:                    case WEEKS:
1535:                        return type.isSupported(DurationFieldType.weeks());
1536:                    case DAYS:
1537:                        return type.isSupported(DurationFieldType.days());
1538:                    case HOURS:
1539:                        return type.isSupported(DurationFieldType.hours());
1540:                    case MINUTES:
1541:                        return type.isSupported(DurationFieldType.minutes());
1542:                    case SECONDS:
1543:                        return type.isSupported(DurationFieldType.seconds());
1544:                    case MILLIS:
1545:                        return type.isSupported(DurationFieldType.millis());
1546:                    case SECONDS_MILLIS: // drop through
1547:                    case SECONDS_OPTIONAL_MILLIS:
1548:                        return type.isSupported(DurationFieldType.seconds())
1549:                                || type.isSupported(DurationFieldType.millis());
1550:                    }
1551:                }
1552:
1553:                void setFieldValue(ReadWritablePeriod period, int field,
1554:                        int value) {
1555:                    switch (field) {
1556:                    default:
1557:                        break;
1558:                    case YEARS:
1559:                        period.setYears(value);
1560:                        break;
1561:                    case MONTHS:
1562:                        period.setMonths(value);
1563:                        break;
1564:                    case WEEKS:
1565:                        period.setWeeks(value);
1566:                        break;
1567:                    case DAYS:
1568:                        period.setDays(value);
1569:                        break;
1570:                    case HOURS:
1571:                        period.setHours(value);
1572:                        break;
1573:                    case MINUTES:
1574:                        period.setMinutes(value);
1575:                        break;
1576:                    case SECONDS:
1577:                        period.setSeconds(value);
1578:                        break;
1579:                    case MILLIS:
1580:                        period.setMillis(value);
1581:                        break;
1582:                    }
1583:                }
1584:
1585:                int getFieldType() {
1586:                    return iFieldType;
1587:                }
1588:            }
1589:
1590:            //-----------------------------------------------------------------------
1591:            /**
1592:             * Handles a simple literal piece of text.
1593:             */
1594:            static class Literal implements  PeriodPrinter, PeriodParser {
1595:                static final Literal EMPTY = new Literal("");
1596:                private final String iText;
1597:
1598:                Literal(String text) {
1599:                    iText = text;
1600:                }
1601:
1602:                public int countFieldsToPrint(ReadablePeriod period,
1603:                        int stopAt, Locale locale) {
1604:                    return 0;
1605:                }
1606:
1607:                public int calculatePrintedLength(ReadablePeriod period,
1608:                        Locale locale) {
1609:                    return iText.length();
1610:                }
1611:
1612:                public void printTo(StringBuffer buf, ReadablePeriod period,
1613:                        Locale locale) {
1614:                    buf.append(iText);
1615:                }
1616:
1617:                public void printTo(Writer out, ReadablePeriod period,
1618:                        Locale locale) throws IOException {
1619:                    out.write(iText);
1620:                }
1621:
1622:                public int parseInto(ReadWritablePeriod period,
1623:                        String periodStr, int position, Locale locale) {
1624:                    if (periodStr.regionMatches(true, position, iText, 0, iText
1625:                            .length())) {
1626:                        return position + iText.length();
1627:                    }
1628:                    return ~position;
1629:                }
1630:            }
1631:
1632:            //-----------------------------------------------------------------------
1633:            /**
1634:             * Handles a separator, that splits the fields into multiple parts.
1635:             * For example, the 'T' in the ISO8601 standard.
1636:             */
1637:            static class Separator implements  PeriodPrinter, PeriodParser {
1638:                private final String iText;
1639:                private final String iFinalText;
1640:                private final String[] iParsedForms;
1641:
1642:                private final boolean iUseBefore;
1643:                private final boolean iUseAfter;
1644:
1645:                private PeriodPrinter iBeforePrinter;
1646:                private PeriodPrinter iAfterPrinter;
1647:                private PeriodParser iBeforeParser;
1648:                private PeriodParser iAfterParser;
1649:
1650:                Separator(String text, String finalText, String[] variants,
1651:                        PeriodPrinter beforePrinter, PeriodParser beforeParser,
1652:                        boolean useBefore, boolean useAfter) {
1653:                    iText = text;
1654:                    iFinalText = finalText;
1655:
1656:                    if ((finalText == null || text.equals(finalText))
1657:                            && (variants == null || variants.length == 0)) {
1658:
1659:                        iParsedForms = new String[] { text };
1660:                    } else {
1661:                        // Filter and reverse sort the parsed forms.
1662:                        TreeSet parsedSet = new TreeSet(
1663:                                String.CASE_INSENSITIVE_ORDER);
1664:                        parsedSet.add(text);
1665:                        parsedSet.add(finalText);
1666:                        if (variants != null) {
1667:                            for (int i = variants.length; --i >= 0;) {
1668:                                parsedSet.add(variants[i]);
1669:                            }
1670:                        }
1671:                        ArrayList parsedList = new ArrayList(parsedSet);
1672:                        Collections.reverse(parsedList);
1673:                        iParsedForms = (String[]) parsedList
1674:                                .toArray(new String[parsedList.size()]);
1675:                    }
1676:
1677:                    iBeforePrinter = beforePrinter;
1678:                    iBeforeParser = beforeParser;
1679:                    iUseBefore = useBefore;
1680:                    iUseAfter = useAfter;
1681:                }
1682:
1683:                public int countFieldsToPrint(ReadablePeriod period,
1684:                        int stopAt, Locale locale) {
1685:                    int sum = iBeforePrinter.countFieldsToPrint(period, stopAt,
1686:                            locale);
1687:                    if (sum < stopAt) {
1688:                        sum += iAfterPrinter.countFieldsToPrint(period, stopAt,
1689:                                locale);
1690:                    }
1691:                    return sum;
1692:                }
1693:
1694:                public int calculatePrintedLength(ReadablePeriod period,
1695:                        Locale locale) {
1696:                    PeriodPrinter before = iBeforePrinter;
1697:                    PeriodPrinter after = iAfterPrinter;
1698:
1699:                    int sum = before.calculatePrintedLength(period, locale)
1700:                            + after.calculatePrintedLength(period, locale);
1701:
1702:                    if (iUseBefore) {
1703:                        if (before.countFieldsToPrint(period, 1, locale) > 0) {
1704:                            if (iUseAfter) {
1705:                                int afterCount = after.countFieldsToPrint(
1706:                                        period, 2, locale);
1707:                                if (afterCount > 0) {
1708:                                    sum += (afterCount > 1 ? iText : iFinalText)
1709:                                            .length();
1710:                                }
1711:                            } else {
1712:                                sum += iText.length();
1713:                            }
1714:                        }
1715:                    } else if (iUseAfter
1716:                            && after.countFieldsToPrint(period, 1, locale) > 0) {
1717:                        sum += iText.length();
1718:                    }
1719:
1720:                    return sum;
1721:                }
1722:
1723:                public void printTo(StringBuffer buf, ReadablePeriod period,
1724:                        Locale locale) {
1725:                    PeriodPrinter before = iBeforePrinter;
1726:                    PeriodPrinter after = iAfterPrinter;
1727:
1728:                    before.printTo(buf, period, locale);
1729:                    if (iUseBefore) {
1730:                        if (before.countFieldsToPrint(period, 1, locale) > 0) {
1731:                            if (iUseAfter) {
1732:                                int afterCount = after.countFieldsToPrint(
1733:                                        period, 2, locale);
1734:                                if (afterCount > 0) {
1735:                                    buf.append(afterCount > 1 ? iText
1736:                                            : iFinalText);
1737:                                }
1738:                            } else {
1739:                                buf.append(iText);
1740:                            }
1741:                        }
1742:                    } else if (iUseAfter
1743:                            && after.countFieldsToPrint(period, 1, locale) > 0) {
1744:                        buf.append(iText);
1745:                    }
1746:                    after.printTo(buf, period, locale);
1747:                }
1748:
1749:                public void printTo(Writer out, ReadablePeriod period,
1750:                        Locale locale) throws IOException {
1751:                    PeriodPrinter before = iBeforePrinter;
1752:                    PeriodPrinter after = iAfterPrinter;
1753:
1754:                    before.printTo(out, period, locale);
1755:                    if (iUseBefore) {
1756:                        if (before.countFieldsToPrint(period, 1, locale) > 0) {
1757:                            if (iUseAfter) {
1758:                                int afterCount = after.countFieldsToPrint(
1759:                                        period, 2, locale);
1760:                                if (afterCount > 0) {
1761:                                    out.write(afterCount > 1 ? iText
1762:                                            : iFinalText);
1763:                                }
1764:                            } else {
1765:                                out.write(iText);
1766:                            }
1767:                        }
1768:                    } else if (iUseAfter
1769:                            && after.countFieldsToPrint(period, 1, locale) > 0) {
1770:                        out.write(iText);
1771:                    }
1772:                    after.printTo(out, period, locale);
1773:                }
1774:
1775:                public int parseInto(ReadWritablePeriod period,
1776:                        String periodStr, int position, Locale locale) {
1777:                    int oldPos = position;
1778:                    position = iBeforeParser.parseInto(period, periodStr,
1779:                            position, locale);
1780:
1781:                    if (position < 0) {
1782:                        return position;
1783:                    }
1784:
1785:                    boolean found = false;
1786:                    if (position > oldPos) {
1787:                        // Consume this separator.
1788:                        String[] parsedForms = iParsedForms;
1789:                        int length = parsedForms.length;
1790:                        for (int i = 0; i < length; i++) {
1791:                            String parsedForm = parsedForms[i];
1792:                            if ((parsedForm == null || parsedForm.length() == 0)
1793:                                    || periodStr.regionMatches(true, position,
1794:                                            parsedForm, 0, parsedForm.length())) {
1795:
1796:                                position += parsedForm.length();
1797:                                found = true;
1798:                                break;
1799:                            }
1800:                        }
1801:                    }
1802:
1803:                    oldPos = position;
1804:                    position = iAfterParser.parseInto(period, periodStr,
1805:                            position, locale);
1806:
1807:                    if (position < 0) {
1808:                        return position;
1809:                    }
1810:
1811:                    if (found && position == oldPos) {
1812:                        // Separator should not have been supplied.
1813:                        return ~oldPos;
1814:                    }
1815:
1816:                    if (position > oldPos && !found && !iUseBefore) {
1817:                        // Separator was required.
1818:                        return ~oldPos;
1819:                    }
1820:
1821:                    return position;
1822:                }
1823:
1824:                Separator finish(PeriodPrinter afterPrinter,
1825:                        PeriodParser afterParser) {
1826:                    iAfterPrinter = afterPrinter;
1827:                    iAfterParser = afterParser;
1828:                    return this ;
1829:                }
1830:            }
1831:
1832:            //-----------------------------------------------------------------------
1833:            /**
1834:             * Composite implementation that merges other fields to create a full pattern.
1835:             */
1836:            static class Composite implements  PeriodPrinter, PeriodParser {
1837:
1838:                private final PeriodPrinter[] iPrinters;
1839:                private final PeriodParser[] iParsers;
1840:
1841:                Composite(List elementPairs) {
1842:                    List printerList = new ArrayList();
1843:                    List parserList = new ArrayList();
1844:
1845:                    decompose(elementPairs, printerList, parserList);
1846:
1847:                    if (printerList.size() <= 0) {
1848:                        iPrinters = null;
1849:                    } else {
1850:                        iPrinters = (PeriodPrinter[]) printerList
1851:                                .toArray(new PeriodPrinter[printerList.size()]);
1852:                    }
1853:
1854:                    if (parserList.size() <= 0) {
1855:                        iParsers = null;
1856:                    } else {
1857:                        iParsers = (PeriodParser[]) parserList
1858:                                .toArray(new PeriodParser[parserList.size()]);
1859:                    }
1860:                }
1861:
1862:                public int countFieldsToPrint(ReadablePeriod period,
1863:                        int stopAt, Locale locale) {
1864:                    int sum = 0;
1865:                    PeriodPrinter[] printers = iPrinters;
1866:                    for (int i = printers.length; sum < stopAt && --i >= 0;) {
1867:                        sum += printers[i].countFieldsToPrint(period,
1868:                                Integer.MAX_VALUE, locale);
1869:                    }
1870:                    return sum;
1871:                }
1872:
1873:                public int calculatePrintedLength(ReadablePeriod period,
1874:                        Locale locale) {
1875:                    int sum = 0;
1876:                    PeriodPrinter[] printers = iPrinters;
1877:                    for (int i = printers.length; --i >= 0;) {
1878:                        sum += printers[i].calculatePrintedLength(period,
1879:                                locale);
1880:                    }
1881:                    return sum;
1882:                }
1883:
1884:                public void printTo(StringBuffer buf, ReadablePeriod period,
1885:                        Locale locale) {
1886:                    PeriodPrinter[] printers = iPrinters;
1887:                    int len = printers.length;
1888:                    for (int i = 0; i < len; i++) {
1889:                        printers[i].printTo(buf, period, locale);
1890:                    }
1891:                }
1892:
1893:                public void printTo(Writer out, ReadablePeriod period,
1894:                        Locale locale) throws IOException {
1895:                    PeriodPrinter[] printers = iPrinters;
1896:                    int len = printers.length;
1897:                    for (int i = 0; i < len; i++) {
1898:                        printers[i].printTo(out, period, locale);
1899:                    }
1900:                }
1901:
1902:                public int parseInto(ReadWritablePeriod period,
1903:                        String periodStr, int position, Locale locale) {
1904:                    PeriodParser[] parsers = iParsers;
1905:                    if (parsers == null) {
1906:                        throw new UnsupportedOperationException();
1907:                    }
1908:
1909:                    int len = parsers.length;
1910:                    for (int i = 0; i < len && position >= 0; i++) {
1911:                        position = parsers[i].parseInto(period, periodStr,
1912:                                position, locale);
1913:                    }
1914:                    return position;
1915:                }
1916:
1917:                private void decompose(List elementPairs, List printerList,
1918:                        List parserList) {
1919:                    int size = elementPairs.size();
1920:                    for (int i = 0; i < size; i += 2) {
1921:                        Object element = elementPairs.get(i);
1922:                        if (element instanceof  PeriodPrinter) {
1923:                            if (element instanceof  Composite) {
1924:                                addArrayToList(printerList,
1925:                                        ((Composite) element).iPrinters);
1926:                            } else {
1927:                                printerList.add(element);
1928:                            }
1929:                        }
1930:
1931:                        element = elementPairs.get(i + 1);
1932:                        if (element instanceof  PeriodParser) {
1933:                            if (element instanceof  Composite) {
1934:                                addArrayToList(parserList,
1935:                                        ((Composite) element).iParsers);
1936:                            } else {
1937:                                parserList.add(element);
1938:                            }
1939:                        }
1940:                    }
1941:                }
1942:
1943:                private void addArrayToList(List list, Object[] array) {
1944:                    if (array != null) {
1945:                        for (int i = 0; i < array.length; i++) {
1946:                            list.add(array[i]);
1947:                        }
1948:                    }
1949:                }
1950:            }
1951:
1952:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.