Source Code Cross Referenced for DateTimePatternGenerator.java in  » Internationalization-Localization » icu4j » com » ibm » icu » text » 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 » Internationalization Localization » icu4j » com.ibm.icu.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        //##header
0002:        //#ifndef FOUNDATION
0003:        /*
0004:         *******************************************************************************
0005:         * Copyright (C) 2006, Google, International Business Machines Corporation and    *
0006:         * others. All Rights Reserved.                                                *
0007:         *******************************************************************************
0008:         *
0009:         * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/text/DateTimePatternGenerator.java,v $
0010:         * $Date: 2006/09/15 18:09:24 $
0011:         * $Revision: 1.9 $
0012:         *
0013:         *******************************************************************************
0014:         */
0015:        package com.ibm.icu.text;
0016:
0017:        import java.util.ArrayList;
0018:        import java.util.Arrays;
0019:        import java.util.BitSet;
0020:        import java.util.Collection;
0021:        import java.util.Collections;
0022:        import java.util.Enumeration;
0023:        import java.util.HashMap;
0024:        import java.util.HashSet;
0025:        import java.util.Iterator;
0026:        import java.util.LinkedHashMap;
0027:        import java.util.LinkedHashSet;
0028:        import java.util.List;
0029:        import java.util.Map;
0030:        import java.util.Set;
0031:        import java.util.TreeMap;
0032:        import java.util.TreeSet;
0033:
0034:        //import org.unicode.cldr.util.Utility;
0035:
0036:        import com.ibm.icu.impl.CalendarData;
0037:        import com.ibm.icu.impl.PatternTokenizer;
0038:        import com.ibm.icu.impl.Utility;
0039:        import com.ibm.icu.text.MessageFormat;
0040:        import com.ibm.icu.text.Transliterator;
0041:        import com.ibm.icu.text.UnicodeSet;
0042:        import com.ibm.icu.util.Calendar;
0043:        import com.ibm.icu.util.Freezable;
0044:        import com.ibm.icu.util.ULocale;
0045:        import com.ibm.icu.util.UResourceBundle;
0046:
0047:        /**
0048:         * This class provides flexible generation of date format patterns, like "yy-MM-dd". The user can build up the generator
0049:         * by adding successive patterns. Once that is done, a query can be made using a "skeleton", which is a pattern which just
0050:         * includes the desired fields and lengths. The generator will return the "best fit" pattern corresponding to that skeleton.
0051:         * <p>The main method people will use is getBestPattern(String skeleton),
0052:         * since normally this class is pre-built with data from a particular locale. However, generators can be built directly from other data as well.
0053:         * <p><i>Issue: may be useful to also have a function that returns the list of fields in a pattern, in order, since we have that internally.
0054:         * That would be useful for getting the UI order of field elements.</i>
0055:         * @draft ICU 3.6
0056:         * @provisional This API might change or be removed in a future release.
0057:         */
0058:        public class DateTimePatternGenerator implements  Freezable, Cloneable {
0059:            // debugging flags
0060:            //static boolean SHOW_DISTANCE = false;
0061:            // TODO add hack to fix months for CJK, as per bug 1099
0062:            // http://dev.icu-project.org/cgi-bin/locale-bugs/incoming?findid=1099
0063:
0064:            /**
0065:             * Create empty generator, to be constructed with add(...) etc.
0066:             * @draft ICU 3.6
0067:             * @provisional This API might change or be removed in a future release.
0068:             */
0069:            public static DateTimePatternGenerator newInstance() {
0070:                return new DateTimePatternGenerator();
0071:            }
0072:
0073:            /**
0074:             * Only for use by subclasses
0075:             * @draft ICU 3.6
0076:             * @provisional This API might change or be removed in a future release.
0077:             */
0078:            protected DateTimePatternGenerator() {
0079:            }
0080:
0081:            /**
0082:             * Construct a flexible generator according to data for a given locale.
0083:             * @draft ICU 3.6
0084:             * @provisional This API might change or be removed in a future release.
0085:             */
0086:            public static DateTimePatternGenerator getInstance() {
0087:                return getInstance(ULocale.getDefault());
0088:            }
0089:
0090:            /**
0091:             * Construct a flexible generator according to data for a given locale.
0092:             * @param uLocale
0093:             * @draft ICU 3.6
0094:             * @provisional This API might change or be removed in a future release.
0095:             */
0096:            public static DateTimePatternGenerator getInstance(ULocale uLocale) {
0097:                DateTimePatternGenerator result = new DateTimePatternGenerator();
0098:                PatternInfo returnInfo = new PatternInfo();
0099:                String hackPattern = null;
0100:                // first load with the ICU patterns
0101:                for (int i = DateFormat.FULL; i <= DateFormat.SHORT; ++i) {
0102:                    SimpleDateFormat df = (SimpleDateFormat) DateFormat
0103:                            .getDateInstance(i, uLocale);
0104:                    result.add(df.toPattern(), false, returnInfo);
0105:                    df = (SimpleDateFormat) DateFormat.getTimeInstance(i,
0106:                            uLocale);
0107:                    result.add(df.toPattern(), false, returnInfo);
0108:                    // HACK for hh:ss
0109:                    if (i == DateFormat.MEDIUM) {
0110:                        hackPattern = df.toPattern();
0111:                    }
0112:                }
0113:                UResourceBundle rb = UResourceBundle.getBundleInstance(
0114:                        "com.ibm.icu.impl.data.DateData$MyDateResources",
0115:                        uLocale);
0116:                //ResourceBundle rb = ResourceBundle.getBundle("com.ibm.icu.impl.data.DateData$MyDateResources", ULocale.FRENCH.toLocale());
0117:                for (Enumeration en = rb.getKeys(); en.hasMoreElements();) {
0118:                    String key = (String) en.nextElement();
0119:                    String value = rb.getString(key);
0120:                    String[] keyParts = key.split("/");
0121:                    if (keyParts[0].equals("pattern")) {
0122:                        result.add(value, false, returnInfo);
0123:                    } else if (keyParts[0].equals("append")) {
0124:                        result.setAppendItemFormats(
0125:                                getAppendFormatNumber(keyParts[1]), value);
0126:                    } else if (keyParts[0].equals("field")) {
0127:                        result.setAppendItemNames(
0128:                                getAppendNameNumber(keyParts[1]), value);
0129:                    }
0130:                }
0131:
0132:                // assume it is always big endian (ok for CLDR right now)
0133:                // some languages didn't add mm:ss or HH:mm, so put in a hack to compute that from the short time.
0134:                if (hackPattern != null) {
0135:                    hackTimes(result, returnInfo, hackPattern);
0136:                }
0137:
0138:                // set the datetime pattern. This is ugly code -- there should be a public interface for this
0139:                Calendar cal = Calendar.getInstance(uLocale);
0140:                CalendarData calData = new CalendarData(uLocale, cal.getType());
0141:                String[] patterns = calData.get("DateTimePatterns")
0142:                        .getStringArray();
0143:                result.setDateTimeFormat(patterns[8]);
0144:
0145:                // decimal point for seconds
0146:                DecimalFormatSymbols dfs = new DecimalFormatSymbols(uLocale);
0147:                result.setDecimal(String.valueOf(dfs.getDecimalSeparator()));
0148:                return result;
0149:            }
0150:
0151:            private static void hackTimes(DateTimePatternGenerator result,
0152:                    PatternInfo returnInfo, String hackPattern) {
0153:                result.fp.set(hackPattern);
0154:                String mmss = new String();
0155:                // to get mm:ss, we strip all but mm literal ss
0156:                boolean gotMm = false;
0157:                for (int i = 0; i < result.fp.items.size(); ++i) {
0158:                    Object item = result.fp.items.get(i);
0159:                    if (item instanceof  String) {
0160:                        if (gotMm) {
0161:                            mmss += result.fp.quoteLiteral(item.toString());
0162:                        }
0163:                    } else {
0164:                        char ch = item.toString().charAt(0);
0165:                        if (ch == 'm') {
0166:                            gotMm = true;
0167:                            mmss += item;
0168:                        } else if (ch == 's') {
0169:                            if (!gotMm) {
0170:                                break; // failed
0171:                            }
0172:                            mmss += item;
0173:                            result.add(mmss, false, returnInfo);
0174:                            break;
0175:                        } else if (gotMm || ch == 'z' || ch == 'Z' || ch == 'v'
0176:                                || ch == 'V') {
0177:                            break; // failed
0178:                        }
0179:                    }
0180:                }
0181:                // to get hh:mm, we strip (literal ss) and (literal S)
0182:                // the easiest way to do this is to mark the stuff we want to nuke, then remove it in a second pass.
0183:                BitSet variables = new BitSet();
0184:                BitSet nuke = new BitSet();
0185:                for (int i = 0; i < result.fp.items.size(); ++i) {
0186:                    Object item = result.fp.items.get(i);
0187:                    if (item instanceof  VariableField) {
0188:                        variables.set(i);
0189:                        char ch = item.toString().charAt(0);
0190:                        if (ch == 's' || ch == 'S') {
0191:                            nuke.set(i);
0192:                            for (int j = i - 1; j >= 0; ++j) {
0193:                                if (variables.get(j))
0194:                                    break;
0195:                                nuke.set(i);
0196:                            }
0197:                        }
0198:                    }
0199:                }
0200:                String hhmm = getFilteredPattern(result.fp, nuke);
0201:                result.add(hhmm, false, returnInfo);
0202:            }
0203:
0204:            private static String getFilteredPattern(FormatParser fp,
0205:                    BitSet nuke) {
0206:                String result = new String();
0207:                for (int i = 0; i < fp.items.size(); ++i) {
0208:                    if (nuke.get(i))
0209:                        continue;
0210:                    Object item = fp.items.get(i);
0211:                    if (item instanceof  String) {
0212:                        result += fp.quoteLiteral(item.toString());
0213:                    } else {
0214:                        result += item.toString();
0215:                    }
0216:                }
0217:                return result;
0218:            }
0219:
0220:            private static int getAppendNameNumber(String string) {
0221:                for (int i = 0; i < CLDR_FIELD_NAME.length; ++i) {
0222:                    if (CLDR_FIELD_NAME[i].equals(string))
0223:                        return i;
0224:                }
0225:                return -1;
0226:            }
0227:
0228:            private static int getAppendFormatNumber(String string) {
0229:                for (int i = 0; i < CLDR_FIELD_APPEND.length; ++i) {
0230:                    if (CLDR_FIELD_APPEND[i].equals(string))
0231:                        return i;
0232:                }
0233:                return -1;
0234:
0235:            }
0236:
0237:            /**
0238:             * Return the best pattern matching the input skeleton. It is guaranteed to
0239:             * have all of the fields in the skeleton.
0240:             * 
0241:             * @param skeleton
0242:             *            The skeleton is a pattern containing only the variable fields.
0243:             *            For example, "MMMdd" and "mmhh" are skeletons.
0244:             * @draft ICU 3.6
0245:             * @provisional This API might change or be removed in a future release.
0246:             */
0247:            public String getBestPattern(String skeleton) {
0248:                //if (!isComplete) complete();
0249:                current.set(skeleton, fp);
0250:                String best = getBestRaw(current, -1, _distanceInfo);
0251:                if (_distanceInfo.missingFieldMask == 0
0252:                        && _distanceInfo.extraFieldMask == 0) {
0253:                    // we have a good item. Adjust the field types
0254:                    return adjustFieldTypes(best, current, false);
0255:                }
0256:                int neededFields = current.getFieldMask();
0257:                // otherwise break up by date and time.
0258:                String datePattern = getBestAppending(neededFields & DATE_MASK);
0259:                String timePattern = getBestAppending(neededFields & TIME_MASK);
0260:
0261:                if (datePattern == null)
0262:                    return timePattern == null ? "" : timePattern;
0263:                if (timePattern == null)
0264:                    return datePattern;
0265:                return MessageFormat.format(getDateTimeFormat(), new Object[] {
0266:                        datePattern, timePattern });
0267:            }
0268:
0269:            /**
0270:             * PatternInfo supplies output parameters for add(...). It is used because
0271:             * Java doesn't have real output parameters. It is treated like a struct (eg
0272:             * Point), so all fields are public.
0273:             * 
0274:             * @draft ICU 3.6
0275:             * @provisional This API might change or be removed in a future release.
0276:             */
0277:            public static final class PatternInfo { // struct for return information
0278:                /**
0279:                 * @draft ICU 3.6
0280:                 * @provisional This API might change or be removed in a future release.
0281:                 */
0282:                public static final int OK = 0;
0283:
0284:                /**
0285:                 * @draft ICU 3.6
0286:                 * @provisional This API might change or be removed in a future release.
0287:                 */
0288:                public static final int BASE_CONFLICT = 1;
0289:
0290:                /**
0291:                 * @draft ICU 3.6
0292:                 * @provisional This API might change or be removed in a future release.
0293:                 */
0294:                public static final int CONFLICT = 2;
0295:
0296:                /**
0297:                 * @draft ICU 3.6
0298:                 * @provisional This API might change or be removed in a future release.
0299:                 */
0300:                public int status;
0301:
0302:                /**
0303:                 * @draft ICU 3.6
0304:                 * @provisional This API might change or be removed in a future release.
0305:                 */
0306:                public String conflictingPattern;
0307:
0308:                /**
0309:                 * Simple constructor, since this is treated like a struct.
0310:                 * @draft ICU 3.6
0311:                 * @provisional This API might change or be removed in a future release.
0312:                 */
0313:                public PatternInfo() {
0314:                }
0315:            }
0316:
0317:            static Transliterator fromHex = Transliterator
0318:                    .getInstance("hex-any");
0319:
0320:            /**
0321:             * Adds a pattern to the generator. If the pattern has the same skeleton as
0322:             * an existing pattern, and the override parameter is set, then the previous
0323:             * value is overriden. Otherwise, the previous value is retained. In either
0324:             * case, the conflicting information is returned in PatternInfo.
0325:             * <p>
0326:             * Note that single-field patterns (like "MMM") are automatically added, and
0327:             * don't need to be added explicitly!
0328:             * 
0329:             * @param override
0330:             *            when existing values are to be overridden use true, otherwise
0331:             *            use false.
0332:             * @draft ICU 3.6
0333:             * @provisional This API might change or be removed in a future release.
0334:             */
0335:            public DateTimePatternGenerator add(String pattern,
0336:                    boolean override, PatternInfo returnInfo) {
0337:                checkFrozen();
0338:                if (pattern.indexOf("\\u") >= 0) {
0339:                    String oldPattern = pattern;
0340:                    pattern = fromHex.transliterate(pattern);
0341:                }
0342:                DateTimeMatcher matcher = new DateTimeMatcher()
0343:                        .set(pattern, fp);
0344:                String basePattern = matcher.getBasePattern();
0345:                String previousPatternWithSameBase = (String) basePattern_pattern
0346:                        .get(basePattern);
0347:                if (previousPatternWithSameBase != null) {
0348:                    returnInfo.status = PatternInfo.BASE_CONFLICT;
0349:                    returnInfo.conflictingPattern = previousPatternWithSameBase;
0350:                    if (!override)
0351:                        return this ;
0352:                }
0353:                String previousValue = (String) skeleton2pattern.get(matcher);
0354:                if (previousValue != null) {
0355:                    returnInfo.status = PatternInfo.CONFLICT;
0356:                    returnInfo.conflictingPattern = previousValue;
0357:                    if (!override)
0358:                        return this ;
0359:                }
0360:                returnInfo.status = PatternInfo.OK;
0361:                returnInfo.conflictingPattern = "";
0362:                skeleton2pattern.put(matcher, pattern);
0363:                basePattern_pattern.put(basePattern, pattern);
0364:                return this ;
0365:            }
0366:
0367:            /**
0368:             * Utility to return a unique skeleton from a given pattern. For example,
0369:             * both "MMM-dd" and "dd/MMM" produce the skeleton "MMMdd".
0370:             * 
0371:             * @param pattern
0372:             *            Input pattern, such as "dd/MMM"
0373:             * @return skeleton, such as "MMMdd"
0374:             * @draft ICU 3.6
0375:             * @provisional This API might change or be removed in a future release.
0376:             */
0377:            public String getSkeleton(String pattern) {
0378:                synchronized (this ) { // synchronized since a getter must be thread-safe
0379:                    current.set(pattern, fp);
0380:                    return current.toString();
0381:                }
0382:            }
0383:
0384:            /**
0385:             * Utility to return a unique base skeleton from a given pattern. This is
0386:             * the same as the skeleton, except that differences in length are minimized
0387:             * so as to only preserve the difference between string and numeric form. So
0388:             * for example, both "MMM-dd" and "d/MMM" produce the skeleton "MMMd"
0389:             * (notice the single d).
0390:             * 
0391:             * @param pattern
0392:             *            Input pattern, such as "dd/MMM"
0393:             * @return skeleton, such as "MMMdd"
0394:             * @draft ICU 3.6
0395:             * @provisional This API might change or be removed in a future release.
0396:             */
0397:            public String getBaseSkeleton(String pattern) {
0398:                synchronized (this ) { // synchronized since a getter must be thread-safe
0399:                    current.set(pattern, fp);
0400:                    return current.getBasePattern();
0401:                }
0402:            }
0403:
0404:            /**
0405:             * Return a list of all the skeletons (in canonical form) from this class,
0406:             * and the patterns that they map to.
0407:             * 
0408:             * @param result
0409:             *            an output Map in which to place the mapping from skeleton to
0410:             *            pattern. If you want to see the internal order being used,
0411:             *            supply a LinkedHashMap. If the input value is null, then a
0412:             *            LinkedHashMap is allocated.
0413:             *            <p>
0414:             *            <i>Issue: an alternate API would be to just return a list of
0415:             *            the skeletons, and then have a separate routine to get from
0416:             *            skeleton to pattern.</i>
0417:             * @return the input Map containing the values.
0418:             * @draft ICU 3.6
0419:             * @provisional This API might change or be removed in a future release.
0420:             */
0421:            public Map getSkeletons(Map result) {
0422:                if (result == null)
0423:                    result = new LinkedHashMap();
0424:                for (Iterator it = skeleton2pattern.keySet().iterator(); it
0425:                        .hasNext();) {
0426:                    DateTimeMatcher item = (DateTimeMatcher) it.next();
0427:                    String pattern = (String) skeleton2pattern.get(item);
0428:                    if (CANONICAL_SET.contains(pattern))
0429:                        continue;
0430:                    result.put(item.toString(), pattern);
0431:                }
0432:                return result;
0433:            }
0434:
0435:            /**
0436:             * Return a list of all the base skeletons (in canonical form) from this class
0437:             * @draft ICU 3.6
0438:             * @provisional This API might change or be removed in a future release.
0439:             */
0440:            public Set getBaseSkeletons(Set result) {
0441:                if (result == null)
0442:                    result = new HashSet();
0443:                result.addAll(basePattern_pattern.keySet());
0444:                return result;
0445:            }
0446:
0447:            /**
0448:             * Adjusts the field types (width and subtype) of a pattern to match what is
0449:             * in a skeleton. That is, if you supply a pattern like "d-M H:m", and a
0450:             * skeleton of "MMMMddhhmm", then the input pattern is adjusted to be
0451:             * "dd-MMMM hh:mm". This is used internally to get the best match for the
0452:             * input skeleton, but can also be used externally.
0453:             * 
0454:             * @param pattern
0455:             *            input pattern
0456:             * @param skeleton
0457:             * @return pattern adjusted to match the skeleton fields widths and
0458:             *         subtypes.
0459:             * @draft ICU 3.6
0460:             * @provisional This API might change or be removed in a future release.
0461:             */
0462:            public String replaceFieldTypes(String pattern, String skeleton) {
0463:                synchronized (this ) { // synchronized since a getter must be thread-safe
0464:                    return adjustFieldTypes(pattern, current.set(skeleton, fp),
0465:                            false);
0466:                }
0467:            }
0468:
0469:            /**
0470:             * The date time format is a message format pattern used to compose date and
0471:             * time patterns. The default value is "{0} {1}", where {0} will be replaced
0472:             * by the date pattern and {1} will be replaced by the time pattern.
0473:             * <p>
0474:             * This is used when the input skeleton contains both date and time fields,
0475:             * but there is not a close match among the added patterns. For example,
0476:             * suppose that this object was created by adding "dd-MMM" and "hh:mm", and
0477:             * its datetimeFormat is the default "{0} {1}". Then if the input skeleton
0478:             * is "MMMdhmm", there is not an exact match, so the input skeleton is
0479:             * broken up into two components "MMMd" and "hmm". There are close matches
0480:             * for those two skeletons, so the result is put together with this pattern,
0481:             * resulting in "d-MMM h:mm".
0482:             * 
0483:             * @param dateTimeFormat
0484:             *            message format pattern, here {0} will be replaced by the date
0485:             *            pattern and {1} will be replaced by the time pattern.
0486:             * @draft ICU 3.6
0487:             * @provisional This API might change or be removed in a future release.
0488:             */
0489:            public void setDateTimeFormat(String dateTimeFormat) {
0490:                checkFrozen();
0491:                this .dateTimeFormat = dateTimeFormat;
0492:            }
0493:
0494:            /**
0495:             * Getter corresponding to setDateTimeFormat.
0496:             * 
0497:             * @return pattern
0498:             * @draft ICU 3.6
0499:             * @provisional This API might change or be removed in a future release.
0500:             */
0501:            public String getDateTimeFormat() {
0502:                return dateTimeFormat;
0503:            }
0504:
0505:            /**
0506:             * The decimal value is used in formatting fractions of seconds. If the
0507:             * skeleton contains fractional seconds, then this is used with the
0508:             * fractional seconds. For example, suppose that the input pattern is
0509:             * "hhmmssSSSS", and the best matching pattern internally is "H:mm:ss", and
0510:             * the decimal string is ",". Then the resulting pattern is modified to be
0511:             * "H:mm:ss,SSSS"
0512:             * 
0513:             * @param decimal
0514:             * @draft ICU 3.6
0515:             * @provisional This API might change or be removed in a future release.
0516:             */
0517:            public void setDecimal(String decimal) {
0518:                checkFrozen();
0519:                this .decimal = decimal;
0520:            }
0521:
0522:            /**
0523:             * Getter corresponding to setDecimal.
0524:             * @return string corresponding to the decimal point
0525:             * @draft ICU 3.6
0526:             * @provisional This API might change or be removed in a future release.
0527:             */
0528:            public String getDecimal() {
0529:                return decimal;
0530:            }
0531:
0532:            /**
0533:             * Redundant patterns are those which if removed, make no difference in the
0534:             * resulting getBestPattern values. This method returns a list of them, to
0535:             * help check the consistency of the patterns used to build this generator.
0536:             * 
0537:             * @param output
0538:             *            stores the redundant patterns that are removed. To get these
0539:             *            in internal order, supply a LinkedHashSet. If null, a
0540:             *            collection is allocated.
0541:             * @return the collection with added elements.
0542:             * @deprecated
0543:             * @internal
0544:             */
0545:            public Collection getRedundants(Collection output) {
0546:                synchronized (this ) { // synchronized since a getter must be thread-safe
0547:                    if (output == null)
0548:                        output = new LinkedHashSet();
0549:                    for (Iterator it = skeleton2pattern.keySet().iterator(); it
0550:                            .hasNext();) {
0551:                        DateTimeMatcher current = (DateTimeMatcher) it.next();
0552:                        String pattern = (String) skeleton2pattern.get(current);
0553:                        if (CANONICAL_SET.contains(pattern))
0554:                            continue;
0555:                        skipMatcher = current;
0556:                        String trial = getBestPattern(current.toString());
0557:                        if (trial.equals(pattern)) {
0558:                            output.add(pattern);
0559:                        }
0560:                    }
0561:                    if (false) { // ordered
0562:                        DateTimePatternGenerator results = new DateTimePatternGenerator();
0563:                        PatternInfo pinfo = new PatternInfo();
0564:                        for (Iterator it = skeleton2pattern.keySet().iterator(); it
0565:                                .hasNext();) {
0566:                            DateTimeMatcher current = (DateTimeMatcher) it
0567:                                    .next();
0568:                            String pattern = (String) skeleton2pattern
0569:                                    .get(current);
0570:                            if (CANONICAL_SET.contains(pattern))
0571:                                continue;
0572:                            //skipMatcher = current;
0573:                            String trial = results.getBestPattern(current
0574:                                    .toString());
0575:                            if (trial.equals(pattern)) {
0576:                                output.add(pattern);
0577:                            } else {
0578:                                results.add(pattern, false, pinfo);
0579:                            }
0580:                        }
0581:                    }
0582:                    return output;
0583:                }
0584:            }
0585:
0586:            // Field numbers, used for AppendItem functions
0587:
0588:            /** 
0589:             * @draft ICU 3.6
0590:             * @provisional This API might change or be removed in a future release.
0591:             */
0592:            static final public int ERA = 0;
0593:
0594:            /**
0595:             * @draft ICU 3.6
0596:             * @provisional This API might change or be removed in a future release.
0597:             */
0598:            static final public int YEAR = 1;
0599:
0600:            /**
0601:             * @draft ICU 3.6
0602:             * @provisional This API might change or be removed in a future release.
0603:             */
0604:            static final public int QUARTER = 2;
0605:
0606:            /**
0607:             * @draft ICU 3.6
0608:             * @provisional This API might change or be removed in a future release.
0609:             */
0610:            static final public int MONTH = 3;
0611:
0612:            /**
0613:             * @draft ICU 3.6
0614:             * @provisional This API might change or be removed in a future release.
0615:             */
0616:            static final public int WEEK_OF_YEAR = 4;
0617:
0618:            /**
0619:             * @draft ICU 3.6
0620:             * @provisional This API might change or be removed in a future release.
0621:             */
0622:            static final public int WEEK_OF_MONTH = 5;
0623:
0624:            /**
0625:             * @draft ICU 3.6
0626:             * @provisional This API might change or be removed in a future release.
0627:             */
0628:            static final public int WEEKDAY = 6;
0629:
0630:            /**
0631:             * @draft ICU 3.6
0632:             * @provisional This API might change or be removed in a future release.
0633:             */
0634:            static final public int DAY = 7;
0635:
0636:            /**
0637:             * @draft ICU 3.6
0638:             * @provisional This API might change or be removed in a future release.
0639:             */
0640:            static final public int DAY_OF_YEAR = 8;
0641:
0642:            /**
0643:             * @draft ICU 3.6
0644:             * @provisional This API might change or be removed in a future release.
0645:             */
0646:            static final public int DAY_OF_WEEK_IN_MONTH = 9;
0647:
0648:            /**
0649:             * @draft ICU 3.6
0650:             * @provisional This API might change or be removed in a future release.
0651:             */
0652:            static final public int DAYPERIOD = 10;
0653:
0654:            /**
0655:             * @draft ICU 3.6
0656:             * @provisional This API might change or be removed in a future release.
0657:             */
0658:            static final public int HOUR = 11;
0659:
0660:            /**
0661:             * @draft ICU 3.6
0662:             * @provisional This API might change or be removed in a future release.
0663:             */
0664:            static final public int MINUTE = 12;
0665:
0666:            /**
0667:             * @draft ICU 3.6
0668:             * @provisional This API might change or be removed in a future release.
0669:             */
0670:            static final public int SECOND = 13;
0671:
0672:            /**
0673:             * @draft ICU 3.6
0674:             * @provisional This API might change or be removed in a future release.
0675:             */
0676:            static final public int FRACTIONAL_SECOND = 14;
0677:
0678:            /**
0679:             * @draft ICU 3.6
0680:             * @provisional This API might change or be removed in a future release.
0681:             */
0682:            static final public int ZONE = 15;
0683:
0684:            /**
0685:             * @draft ICU 3.6
0686:             * @provisional This API might change or be removed in a future release.
0687:             */
0688:            static final public int TYPE_LIMIT = 16;
0689:
0690:            /**
0691:             * An AppendItem format is a pattern used to append a field if there is no
0692:             * good match. For example, suppose that the input skeleton is "GyyyyMMMd",
0693:             * and there is no matching pattern internally, but there is a pattern
0694:             * matching "yyyyMMMd", say "d-MM-yyyy". Then that pattern is used, plus the
0695:             * G. The way these two are conjoined is by using the AppendItemFormat for G
0696:             * (era). So if that value is, say "{0}, {1}" then the final resulting
0697:             * pattern is "d-MM-yyyy, G".
0698:             * <p>
0699:             * There are actually three available variables: {0} is the pattern so far,
0700:             * {1} is the element we are adding, and {2} is the name of the element.
0701:             * <p>
0702:             * This reflects the way that the CLDR data is organized.
0703:             * 
0704:             * @param field
0705:             *            such as ERA
0706:             * @param value
0707:             *            pattern, such as "{0}, {1}"
0708:             * @draft ICU 3.6
0709:             * @provisional This API might change or be removed in a future release.
0710:             */
0711:            public void setAppendItemFormats(int field, String value) {
0712:                checkFrozen();
0713:                appendItemFormats[field] = value;
0714:            }
0715:
0716:            /**
0717:             * Getter corresponding to setAppendItemFormats. Values below 0 or at or
0718:             * above TYPE_LIMIT are illegal arguments.
0719:             * 
0720:             * @param field
0721:             * @return append pattern for field
0722:             * @draft ICU 3.6
0723:             * @provisional This API might change or be removed in a future release.
0724:             */
0725:            public String getAppendItemFormats(int field) {
0726:                return appendItemFormats[field];
0727:            }
0728:
0729:            /**
0730:             * Sets the names of fields, eg "era" in English for ERA. These are only
0731:             * used if the corresponding AppendItemFormat is used, and if it contains a
0732:             * {2} variable.
0733:             * <p>
0734:             * This reflects the way that the CLDR data is organized.
0735:             * 
0736:             * @param field
0737:             * @param value
0738:             * @draft ICU 3.6
0739:             * @provisional This API might change or be removed in a future release.
0740:             */
0741:            public void setAppendItemNames(int field, String value) {
0742:                checkFrozen();
0743:                appendItemNames[field] = value;
0744:            }
0745:
0746:            /**
0747:             * Getter corresponding to setAppendItemNames. Values below 0 or at or above
0748:             * TYPE_LIMIT are illegal arguments.
0749:             * 
0750:             * @param field
0751:             * @return name for field
0752:             * @draft ICU 3.6
0753:             * @provisional This API might change or be removed in a future release.
0754:             */
0755:            public String getAppendItemNames(int field) {
0756:                return appendItemNames[field];
0757:            }
0758:
0759:            /**
0760:             * Determines whether a skeleton contains a single field
0761:             * 
0762:             * @param skeleton
0763:             * @return true or not
0764:             * @deprecated
0765:             * @internal
0766:             */
0767:            public static boolean isSingleField(String skeleton) {
0768:                char first = skeleton.charAt(0);
0769:                for (int i = 1; i < skeleton.length(); ++i) {
0770:                    if (skeleton.charAt(i) != first)
0771:                        return false;
0772:                }
0773:                return true;
0774:            }
0775:
0776:            /**
0777:             * Boilerplate for Freezable
0778:             * @draft ICU 3.6
0779:             * @provisional This API might change or be removed in a future release.
0780:             */
0781:            public boolean isFrozen() {
0782:                return frozen;
0783:            }
0784:
0785:            /**
0786:             * Boilerplate for Freezable
0787:             * @draft ICU 3.6
0788:             * @provisional This API might change or be removed in a future release.
0789:             */
0790:            public Object freeze() {
0791:                frozen = true;
0792:                return this ;
0793:            }
0794:
0795:            /**
0796:             * Boilerplate for Freezable
0797:             * @draft ICU 3.6
0798:             * @provisional This API might change or be removed in a future release.
0799:             */
0800:            public Object cloneAsThawed() {
0801:                DateTimePatternGenerator result = (DateTimePatternGenerator) (this 
0802:                        .clone());
0803:                frozen = false;
0804:                return result;
0805:            }
0806:
0807:            /**
0808:             * Boilerplate
0809:             * @draft ICU 3.6
0810:             * @provisional This API might change or be removed in a future release.
0811:             */
0812:            public Object clone() {
0813:                try {
0814:                    DateTimePatternGenerator result = (DateTimePatternGenerator) (super 
0815:                            .clone());
0816:                    result.skeleton2pattern = (TreeMap) skeleton2pattern
0817:                            .clone();
0818:                    result.basePattern_pattern = (TreeMap) basePattern_pattern
0819:                            .clone();
0820:                    result.appendItemFormats = (String[]) appendItemFormats
0821:                            .clone();
0822:                    result.appendItemNames = (String[]) appendItemNames.clone();
0823:                    result.current = new DateTimeMatcher();
0824:                    result.fp = new FormatParser();
0825:                    result._distanceInfo = new DistanceInfo();
0826:
0827:                    result.frozen = false;
0828:                    return result;
0829:                } catch (CloneNotSupportedException e) {
0830:                    throw new IllegalArgumentException("Internal Error");
0831:                }
0832:            }
0833:
0834:            /**
0835:             * Utility class for FormatParser. Immutable class.
0836:             * @deprecated
0837:             * @internal
0838:             */
0839:            public static class VariableField {
0840:                private String string;
0841:
0842:                /**
0843:                 * Create a variable field
0844:                 * @param string
0845:                 * @deprecated
0846:                 * @internal
0847:                 */
0848:                public VariableField(String string) {
0849:                    this .string = string;
0850:                }
0851:
0852:                /**
0853:                 * Get the internal results
0854:                 * @deprecated
0855:                 * @internal
0856:                 */
0857:                public String toString() {
0858:                    return string;
0859:                }
0860:            }
0861:
0862:            /**
0863:             * Class providing date formatting
0864:             * @deprecated
0865:             * @internal
0866:             */
0867:            static public class FormatParser {
0868:                private transient PatternTokenizer tokenizer = new PatternTokenizer()
0869:                        .setSyntaxCharacters(new UnicodeSet("[a-zA-Z]"))
0870:                        //.setEscapeCharacters(new UnicodeSet("[^\\u0020-\\u007E]")) // WARNING: DateFormat doesn't accept \\uXXXX
0871:                        .setUsingQuote(true);
0872:                private List items = new ArrayList();
0873:
0874:                /**
0875:                 * Set the string to parse
0876:                 * @param string
0877:                 * @return this, for chaining
0878:                 * @deprecated
0879:                 * @internal
0880:                 */
0881:                public FormatParser set(String string) {
0882:                    items.clear();
0883:                    if (string.length() == 0)
0884:                        return this ;
0885:                    tokenizer.setPattern(string);
0886:                    StringBuffer buffer = new StringBuffer();
0887:                    StringBuffer variable = new StringBuffer();
0888:                    while (true) {
0889:                        buffer.setLength(0);
0890:                        int status = tokenizer.next(buffer);
0891:                        if (status == PatternTokenizer.DONE)
0892:                            break;
0893:                        if (status == PatternTokenizer.SYNTAX) {
0894:                            if (variable.length() != 0
0895:                                    && buffer.charAt(0) != variable.charAt(0)) {
0896:                                addVariable(variable);
0897:                            }
0898:                            variable.append(buffer);
0899:                        } else {
0900:                            addVariable(variable);
0901:                            items.add(buffer.toString());
0902:                        }
0903:                    }
0904:                    addVariable(variable);
0905:                    return this ;
0906:                }
0907:
0908:                private void addVariable(StringBuffer variable) {
0909:                    if (variable.length() != 0) {
0910:                        items.add(new VariableField(variable.toString()));
0911:                        variable.setLength(0);
0912:                    }
0913:                }
0914:
0915:                /** Return a collection of fields. These will be a mixture of Strings and VariableFields. Any "a" variable field is removed.
0916:                 * @param output List to append the items to. If null, is allocated as an ArrayList.
0917:                 * @return list
0918:                 */
0919:                private List getVariableFields(List output) {
0920:                    if (output == null)
0921:                        output = new ArrayList();
0922:                    main: for (Iterator it = items.iterator(); it.hasNext();) {
0923:                        Object item = it.next();
0924:                        if (item instanceof  VariableField) {
0925:                            String s = item.toString();
0926:                            switch (s.charAt(0)) {
0927:                            //case 'Q': continue main; // HACK
0928:                            case 'a':
0929:                                continue main; // remove
0930:                            }
0931:                            output.add(s);
0932:                        }
0933:                    }
0934:                    //System.out.println(output);
0935:                    return output;
0936:                }
0937:
0938:                /**
0939:                 * @return a string which is a concatenation of all the variable fields
0940:                 * @deprecated
0941:                 * @internal
0942:                 */
0943:                public String getVariableFieldString() {
0944:                    List list = getVariableFields(null);
0945:                    StringBuffer result = new StringBuffer();
0946:                    for (Iterator it = list.iterator(); it.hasNext();) {
0947:                        String item = (String) it.next();
0948:                        result.append(item);
0949:                    }
0950:                    return result.toString();
0951:                }
0952:
0953:                /**
0954:                 * Returns modifiable list which is a mixture of Strings and VariableFields, in the order found during parsing.
0955:                 * @return modifiable list of items.
0956:                 * @deprecated
0957:                 * @internal
0958:                 */
0959:                public List getItems() {
0960:                    return items;
0961:                }
0962:
0963:                /** Provide display form of formatted input
0964:                 * @return printable output string
0965:                 * @deprecated
0966:                 * @internal
0967:                 */
0968:                public String toString() {
0969:                    return toString(0, items.size());
0970:                }
0971:
0972:                /**
0973:                 * Provide display form of formatted input
0974:                 * @param start item to start from
0975:                 * @param limit last item +1
0976:                 * @return printable output string
0977:                 * @deprecated
0978:                 * @internal
0979:                 */
0980:                public String toString(int start, int limit) {
0981:                    StringBuffer result = new StringBuffer();
0982:                    for (int i = start; i < limit; ++i) {
0983:                        result.append(items.get(i).toString());
0984:                    }
0985:                    return result.toString();
0986:                }
0987:
0988:                /**
0989:                 * Internal method <p>
0990:                 * Returns true if it has a mixture of date and time fields
0991:                 * @return true or false
0992:                 * @deprecated
0993:                 * @internal
0994:                 */
0995:                public boolean hasDateAndTimeFields() {
0996:                    int foundMask = 0;
0997:                    for (Iterator it = items.iterator(); it.hasNext();) {
0998:                        Object item = it.next();
0999:                        if (item instanceof  VariableField) {
1000:                            int type = getType(item);
1001:                            foundMask |= 1 << type;
1002:                        }
1003:                    }
1004:                    boolean isDate = (foundMask & DATE_MASK) != 0;
1005:                    boolean isTime = (foundMask & TIME_MASK) != 0;
1006:                    return isDate && isTime;
1007:                }
1008:
1009:                /**
1010:                 * Internal routine
1011:                 * @param value
1012:                 * @param result
1013:                 * @return list
1014:                 * @deprecated
1015:                 * @internal
1016:                 */
1017:                public List getAutoPatterns(String value, List result) {
1018:                    if (result == null)
1019:                        result = new ArrayList();
1020:                    int fieldCount = 0;
1021:                    int minField = Integer.MAX_VALUE;
1022:                    int maxField = Integer.MIN_VALUE;
1023:                    for (Iterator it = items.iterator(); it.hasNext();) {
1024:                        Object item = it.next();
1025:                        if (item instanceof  VariableField) {
1026:                            try {
1027:                                int type = getType(item);
1028:                                if (minField > type)
1029:                                    minField = type;
1030:                                if (maxField < type)
1031:                                    maxField = type;
1032:                                if (type == ZONE || type == DAYPERIOD
1033:                                        || type == WEEKDAY)
1034:                                    return result; // skip anything with zones                    
1035:                                fieldCount++;
1036:                            } catch (Exception e) {
1037:                                return result; // if there are any funny fields, return
1038:                            }
1039:                        }
1040:                    }
1041:                    if (fieldCount < 3)
1042:                        return result; // skip
1043:                    // trim from start
1044:                    // trim first field IF there are no letters around it
1045:                    // and it is either the min or the max field
1046:                    // first field is either 0 or 1
1047:                    for (int i = 0; i < items.size(); ++i) {
1048:                        Object item = items.get(i);
1049:                        if (item instanceof  VariableField) {
1050:                            int type = getType(item);
1051:                            if (type != minField && type != maxField)
1052:                                break;
1053:
1054:                            if (i > 0) {
1055:                                Object previousItem = items.get(0);
1056:                                if (alpha.containsSome(previousItem.toString()))
1057:                                    break;
1058:                            }
1059:                            int start = i + 1;
1060:                            if (start < items.size()) {
1061:                                Object nextItem = items.get(start);
1062:                                if (nextItem instanceof  String) {
1063:                                    if (alpha.containsSome(nextItem.toString()))
1064:                                        break;
1065:                                    start++; // otherwise skip over string
1066:                                }
1067:                            }
1068:                            result.add(toString(start, items.size()));
1069:                            break;
1070:                        }
1071:                    }
1072:                    // now trim from end
1073:                    for (int i = items.size() - 1; i >= 0; --i) {
1074:                        Object item = items.get(i);
1075:                        if (item instanceof  VariableField) {
1076:                            int type = getType(item);
1077:                            if (type != minField && type != maxField)
1078:                                break;
1079:                            if (i < items.size() - 1) {
1080:                                Object previousItem = items
1081:                                        .get(items.size() - 1);
1082:                                if (alpha.containsSome(previousItem.toString()))
1083:                                    break;
1084:                            }
1085:                            int end = i - 1;
1086:                            if (end > 0) {
1087:                                Object nextItem = items.get(end);
1088:                                if (nextItem instanceof  String) {
1089:                                    if (alpha.containsSome(nextItem.toString()))
1090:                                        break;
1091:                                    end--; // otherwise skip over string
1092:                                }
1093:                            }
1094:                            result.add(toString(0, end + 1));
1095:                            break;
1096:                        }
1097:                    }
1098:
1099:                    return result;
1100:                }
1101:
1102:                private static UnicodeSet alpha = new UnicodeSet(
1103:                        "[:alphabetic:]");
1104:
1105:                private int getType(Object item) {
1106:                    String s = item.toString();
1107:                    int canonicalIndex = getCanonicalIndex(s);
1108:                    if (canonicalIndex < 0) {
1109:                        throw new IllegalArgumentException("Illegal field:\t"
1110:                                + s);
1111:                    }
1112:                    int type = types[canonicalIndex][1];
1113:                    return type;
1114:                }
1115:
1116:                /**
1117:                 *  produce a quoted literal
1118:                 * @param string
1119:                 * @return string with quoted literals
1120:                 * @deprecated
1121:                 * @internal
1122:                 */
1123:                public Object quoteLiteral(String string) {
1124:                    return tokenizer.quoteLiteral(string);
1125:                }
1126:
1127:                /**
1128:                 * Simple constructor, since this is treated like a struct.
1129:                 * @deprecated
1130:                 * @internal
1131:                 */
1132:                public FormatParser() {
1133:                    super ();
1134:                    // TODO Auto-generated constructor stub
1135:                }
1136:            }
1137:
1138:            // ========= PRIVATES ============
1139:
1140:            private TreeMap skeleton2pattern = new TreeMap(); // items are in priority order
1141:            private TreeMap basePattern_pattern = new TreeMap(); // items are in priority order
1142:            private String decimal = "?";
1143:            private String dateTimeFormat = "{0} {1}";
1144:            private String[] appendItemFormats = new String[TYPE_LIMIT];
1145:            private String[] appendItemNames = new String[TYPE_LIMIT];
1146:            {
1147:                for (int i = 0; i < TYPE_LIMIT; ++i) {
1148:                    appendItemFormats[i] = "{0} \u251C{2}: {1}\u2524";
1149:                    appendItemNames[i] = "F" + i;
1150:                }
1151:            }
1152:
1153:            private transient DateTimeMatcher current = new DateTimeMatcher();
1154:            private transient FormatParser fp = new FormatParser();
1155:            private transient DistanceInfo _distanceInfo = new DistanceInfo();
1156:            private transient boolean isComplete = false;
1157:            private transient DateTimeMatcher skipMatcher = null; // only used temporarily, for internal purposes
1158:            private transient boolean frozen = false;
1159:
1160:            private static final int FRACTIONAL_MASK = 1 << FRACTIONAL_SECOND;
1161:            private static final int SECOND_AND_FRACTIONAL_MASK = (1 << SECOND)
1162:                    | (1 << FRACTIONAL_SECOND);
1163:
1164:            private void checkFrozen() {
1165:                if (isFrozen()) {
1166:                    throw new UnsupportedOperationException(
1167:                            "Attempt to modify frozen object");
1168:                }
1169:            }
1170:
1171:            /**
1172:             * We only get called here if we failed to find an exact skeleton. We have broken it into date + time, and look for the pieces.
1173:             * If we fail to find a complete skeleton, we compose in a loop until we have all the fields.
1174:             */
1175:            private String getBestAppending(int missingFields) {
1176:                String resultPattern = null;
1177:                if (missingFields != 0) {
1178:                    resultPattern = getBestRaw(current, missingFields,
1179:                            _distanceInfo);
1180:                    resultPattern = adjustFieldTypes(resultPattern, current,
1181:                            false);
1182:
1183:                    while (_distanceInfo.missingFieldMask != 0) { // precondition: EVERY single field must work!
1184:
1185:                        // special hack for SSS. If we are missing SSS, and we had ss but found it, replace the s field according to the 
1186:                        // number separator
1187:                        if ((_distanceInfo.missingFieldMask & SECOND_AND_FRACTIONAL_MASK) == FRACTIONAL_MASK
1188:                                && (missingFields & SECOND_AND_FRACTIONAL_MASK) == SECOND_AND_FRACTIONAL_MASK) {
1189:                            resultPattern = adjustFieldTypes(resultPattern,
1190:                                    current, true);
1191:                            _distanceInfo.missingFieldMask &= ~FRACTIONAL_MASK; // remove bit
1192:                            continue;
1193:                        }
1194:
1195:                        int startingMask = _distanceInfo.missingFieldMask;
1196:                        String temp = getBestRaw(current,
1197:                                _distanceInfo.missingFieldMask, _distanceInfo);
1198:                        temp = adjustFieldTypes(temp, current, false);
1199:                        int foundMask = startingMask
1200:                                & ~_distanceInfo.missingFieldMask;
1201:                        int topField = getTopBitNumber(foundMask);
1202:                        resultPattern = MessageFormat.format(
1203:                                getAppendFormat(topField), new Object[] {
1204:                                        resultPattern, temp,
1205:                                        getAppendName(topField) });
1206:                    }
1207:                }
1208:                return resultPattern;
1209:            }
1210:
1211:            private String getAppendName(int foundMask) {
1212:                return "'" + appendItemNames[foundMask] + "'";
1213:            }
1214:
1215:            private String getAppendFormat(int foundMask) {
1216:                return appendItemFormats[foundMask];
1217:            }
1218:
1219:            /**
1220:             * @param current2
1221:             * @return
1222:             */
1223:            private String adjustSeconds(DateTimeMatcher current2) {
1224:                // TODO Auto-generated method stub
1225:                return null;
1226:            }
1227:
1228:            /**
1229:             * @param foundMask
1230:             * @return
1231:             */
1232:            private int getTopBitNumber(int foundMask) {
1233:                int i = 0;
1234:                while (foundMask != 0) {
1235:                    foundMask >>>= 1;
1236:                    ++i;
1237:                }
1238:                return i - 1;
1239:            }
1240:
1241:            /**
1242:             * 
1243:             */
1244:            private void complete() {
1245:                PatternInfo patternInfo = new PatternInfo();
1246:                // make sure that every valid field occurs once, with a "default" length
1247:                for (int i = 0; i < CANONICAL_ITEMS.length; ++i) {
1248:                    char c = (char) types[i][0];
1249:                    add(String.valueOf(CANONICAL_ITEMS[i]), false, patternInfo);
1250:                }
1251:                isComplete = true;
1252:            }
1253:
1254:            {
1255:                complete();
1256:            }
1257:
1258:            /**
1259:             * 
1260:             */
1261:            private String getBestRaw(DateTimeMatcher source, int includeMask,
1262:                    DistanceInfo missingFields) {
1263:                //      if (SHOW_DISTANCE) System.out.println("Searching for: " + source.pattern 
1264:                //      + ", mask: " + showMask(includeMask));
1265:                int bestDistance = Integer.MAX_VALUE;
1266:                String bestPattern = "";
1267:                DistanceInfo tempInfo = new DistanceInfo();
1268:                for (Iterator it = skeleton2pattern.keySet().iterator(); it
1269:                        .hasNext();) {
1270:                    DateTimeMatcher trial = (DateTimeMatcher) it.next();
1271:                    if (trial.equals(skipMatcher))
1272:                        continue;
1273:                    int distance = source.getDistance(trial, includeMask,
1274:                            tempInfo);
1275:                    //          if (SHOW_DISTANCE) System.out.println("\tDistance: " + trial.pattern + ":\t" 
1276:                    //          + distance + ",\tmissing fields: " + tempInfo);
1277:                    if (distance < bestDistance) {
1278:                        bestDistance = distance;
1279:                        bestPattern = (String) skeleton2pattern.get(trial);
1280:                        missingFields.setTo(tempInfo);
1281:                        if (distance == 0)
1282:                            break;
1283:                    }
1284:                }
1285:                return bestPattern;
1286:            }
1287:
1288:            /**
1289:             * @param fixFractionalSeconds TODO
1290:             * 
1291:             */
1292:            private String adjustFieldTypes(String pattern,
1293:                    DateTimeMatcher inputRequest, boolean fixFractionalSeconds) {
1294:                fp.set(pattern);
1295:                StringBuffer newPattern = new StringBuffer();
1296:                for (Iterator it = fp.getItems().iterator(); it.hasNext();) {
1297:                    Object item = it.next();
1298:                    if (item instanceof  String) {
1299:                        newPattern.append(fp.quoteLiteral((String) item));
1300:                    } else {
1301:                        String field = ((VariableField) item).string;
1302:                        int canonicalIndex = getCanonicalIndex(field);
1303:                        if (canonicalIndex < 0) {
1304:                            continue; // don't adjust
1305:                        }
1306:                        int type = types[canonicalIndex][1];
1307:                        if (fixFractionalSeconds && type == SECOND) {
1308:                            String newField = inputRequest.original[FRACTIONAL_SECOND];
1309:                            field = field + decimal + newField;
1310:                        } else if (inputRequest.type[type] != 0) {
1311:                            String newField = inputRequest.original[type];
1312:                            // normally we just replace the field. However HOUR is special; we only change the length
1313:                            if (type != HOUR) {
1314:                                field = newField;
1315:                            } else if (field.length() != newField.length()) {
1316:                                char c = field.charAt(0);
1317:                                field = "";
1318:                                for (int i = newField.length(); i > 0; --i)
1319:                                    field += c;
1320:                            }
1321:                        }
1322:                        newPattern.append(field);
1323:                    }
1324:                }
1325:                //if (SHOW_DISTANCE) System.out.println("\tRaw: " + pattern);
1326:                return newPattern.toString();
1327:            }
1328:
1329:            //  public static String repeat(String s, int count) {
1330:            //  StringBuffer result = new StringBuffer();
1331:            //  for (int i = 0; i < count; ++i) {
1332:            //  result.append(s);
1333:            //  }
1334:            //  return result.toString();
1335:            //  }
1336:
1337:            /**
1338:             * internal routine
1339:             * @param pattern
1340:             * @return field value
1341:             * @deprecated
1342:             * @internal
1343:             */
1344:            public String getFields(String pattern) {
1345:                fp.set(pattern);
1346:                StringBuffer newPattern = new StringBuffer();
1347:                for (Iterator it = fp.getItems().iterator(); it.hasNext();) {
1348:                    Object item = it.next();
1349:                    if (item instanceof  String) {
1350:                        newPattern.append(fp.quoteLiteral((String) item));
1351:                    } else {
1352:                        newPattern.append("{" + getName(item.toString()) + "}");
1353:                    }
1354:                }
1355:                return newPattern.toString();
1356:            }
1357:
1358:            private static String showMask(int mask) {
1359:                String result = "";
1360:                for (int i = 0; i < TYPE_LIMIT; ++i) {
1361:                    if ((mask & (1 << i)) == 0)
1362:                        continue;
1363:                    if (result.length() != 0)
1364:                        result += " | ";
1365:                    result += FIELD_NAME[i] + " ";
1366:                }
1367:                return result;
1368:            }
1369:
1370:            static private String[] CLDR_FIELD_APPEND = { "Era", "Year",
1371:                    "Quarter", "Month", "Week", "*", "Day-Of-Week", "Day", "*",
1372:                    "*", "*", "Hour", "Minute", "Second", "*", "Timezone" };
1373:
1374:            static private String[] CLDR_FIELD_NAME = { "era", "year",
1375:                    "quarter", "month", "week", "*", "weekday", "day", "*",
1376:                    "*", "dayperiod", "hour", "minute", "second", "*", "zone" };
1377:
1378:            static private String[] FIELD_NAME = { "Era", "Year", "Quarter",
1379:                    "Month", "Week_in_Year", "Week_in_Month", "Weekday", "Day",
1380:                    "Day_Of_Year", "Day_of_Week_in_Month", "Dayperiod", "Hour",
1381:                    "Minute", "Second", "Fractional_Second", "Zone" };
1382:
1383:            static private String[] CANONICAL_ITEMS = { "G", "y", "Q", "M",
1384:                    "w", "W", "e", "d", "D", "F", "H", "m", "s", "S", "v" };
1385:
1386:            static private Set CANONICAL_SET = new HashSet(Arrays
1387:                    .asList(CANONICAL_ITEMS));
1388:
1389:            static final private int DATE_MASK = (1 << DAYPERIOD) - 1,
1390:                    TIME_MASK = (1 << TYPE_LIMIT) - 1 - DATE_MASK;
1391:
1392:            static final private int // numbers are chosen to express 'distance'
1393:                    DELTA = 0x10,
1394:                    NUMERIC = 0x100, NONE = 0, NARROW = -0x100,
1395:                    SHORT = -0x101,
1396:                    LONG = -0x102,
1397:                    EXTRA_FIELD = 0x10000,
1398:                    MISSING_FIELD = 0x1000;
1399:
1400:            static private String getName(String s) {
1401:                int i = getCanonicalIndex(s);
1402:                String name = FIELD_NAME[types[i][1]];
1403:                int subtype = types[i][2];
1404:                boolean string = subtype < 0;
1405:                if (string)
1406:                    subtype = -subtype;
1407:                if (subtype < 0)
1408:                    name += ":S";
1409:                else
1410:                    name += ":N";
1411:                return name;
1412:            }
1413:
1414:            static private int getCanonicalIndex(String s) {
1415:                int len = s.length();
1416:                int ch = s.charAt(0);
1417:                for (int i = 0; i < types.length; ++i) {
1418:                    int[] row = types[i];
1419:                    if (row[0] != ch)
1420:                        continue;
1421:                    if (row[3] > len)
1422:                        continue;
1423:                    if (row[row.length - 1] < len)
1424:                        continue;
1425:                    return i;
1426:                }
1427:                return -1;
1428:            }
1429:
1430:            static private int[][] types = {
1431:                    // the order here makes a difference only when searching for single field.
1432:                    // format is:
1433:                    // pattern character, main type, weight, min length, weight
1434:                    { 'G', ERA, SHORT, 1, 3 },
1435:                    { 'G', ERA, LONG, 4 },
1436:
1437:                    { 'y', YEAR, NUMERIC, 1, 20 },
1438:                    { 'Y', YEAR, NUMERIC + DELTA, 1, 20 },
1439:                    { 'u', YEAR, NUMERIC + 2 * DELTA, 1, 20 },
1440:
1441:                    { 'Q', QUARTER, NUMERIC, 1, 2 },
1442:                    { 'Q', QUARTER, SHORT, 3 },
1443:                    { 'Q', QUARTER, LONG, 4 },
1444:
1445:                    { 'M', MONTH, NUMERIC, 1, 2 },
1446:                    { 'M', MONTH, SHORT, 3 },
1447:                    { 'M', MONTH, LONG, 4 },
1448:                    { 'M', MONTH, NARROW, 5 },
1449:                    { 'L', MONTH, NUMERIC + DELTA, 1, 2 },
1450:                    { 'L', MONTH, SHORT - DELTA, 3 },
1451:                    { 'L', MONTH, LONG - DELTA, 4 },
1452:                    { 'L', MONTH, NARROW - DELTA, 5 },
1453:
1454:                    { 'w', WEEK_OF_YEAR, NUMERIC, 1, 2 },
1455:                    { 'W', WEEK_OF_MONTH, NUMERIC + DELTA, 1 },
1456:
1457:                    { 'e', WEEKDAY, NUMERIC + DELTA, 1, 2 },
1458:                    { 'e', WEEKDAY, SHORT - DELTA, 3 },
1459:                    { 'e', WEEKDAY, LONG - DELTA, 4 },
1460:                    { 'e', WEEKDAY, NARROW - DELTA, 5 },
1461:                    { 'E', WEEKDAY, SHORT, 1, 3 },
1462:                    { 'E', WEEKDAY, LONG, 4 },
1463:                    { 'E', WEEKDAY, NARROW, 5 },
1464:                    { 'c', WEEKDAY, NUMERIC + 2 * DELTA, 1, 2 },
1465:                    { 'c', WEEKDAY, SHORT - 2 * DELTA, 3 },
1466:                    { 'c', WEEKDAY, LONG - 2 * DELTA, 4 },
1467:                    { 'c', WEEKDAY, NARROW - 2 * DELTA, 5 },
1468:
1469:                    { 'd', DAY, NUMERIC, 1, 2 },
1470:                    { 'D', DAY_OF_YEAR, NUMERIC + DELTA, 1, 3 },
1471:                    { 'F', DAY_OF_WEEK_IN_MONTH, NUMERIC + 2 * DELTA, 1 },
1472:                    { 'g', DAY, NUMERIC + 3 * DELTA, 1, 20 }, // really internal use, so we don't care
1473:
1474:                    { 'a', DAYPERIOD, SHORT, 1 },
1475:
1476:                    { 'H', HOUR, NUMERIC + 10 * DELTA, 1, 2 }, // 24 hour
1477:                    { 'k', HOUR, NUMERIC + 11 * DELTA, 1, 2 },
1478:                    { 'h', HOUR, NUMERIC, 1, 2 }, // 12 hour
1479:                    { 'K', HOUR, NUMERIC + DELTA, 1, 2 },
1480:
1481:                    { 'm', MINUTE, NUMERIC, 1, 2 },
1482:
1483:                    { 's', SECOND, NUMERIC, 1, 2 },
1484:                    { 'S', FRACTIONAL_SECOND, NUMERIC + DELTA, 1, 1000 },
1485:                    { 'A', SECOND, NUMERIC + 2 * DELTA, 1, 1000 },
1486:
1487:                    { 'v', ZONE, SHORT - 2 * DELTA, 1 },
1488:                    { 'v', ZONE, LONG - 2 * DELTA, 4 },
1489:                    { 'z', ZONE, SHORT, 1, 3 }, { 'z', ZONE, LONG, 4 },
1490:                    { 'Z', ZONE, SHORT - DELTA, 1, 3 },
1491:                    { 'Z', ZONE, LONG - DELTA, 4 }, };
1492:
1493:            private static class DateTimeMatcher implements  Comparable {
1494:                //private String pattern = null;
1495:                private int[] type = new int[TYPE_LIMIT];
1496:                private String[] original = new String[TYPE_LIMIT];
1497:                private String[] baseOriginal = new String[TYPE_LIMIT];
1498:
1499:                // just for testing; fix to make multi-threaded later
1500:                // private static FormatParser fp = new FormatParser();
1501:
1502:                public String toString() {
1503:                    StringBuffer result = new StringBuffer();
1504:                    for (int i = 0; i < TYPE_LIMIT; ++i) {
1505:                        if (original[i].length() != 0)
1506:                            result.append(original[i]);
1507:                    }
1508:                    return result.toString();
1509:                }
1510:
1511:                String getBasePattern() {
1512:                    StringBuffer result = new StringBuffer();
1513:                    for (int i = 0; i < TYPE_LIMIT; ++i) {
1514:                        if (baseOriginal[i].length() != 0)
1515:                            result.append(baseOriginal[i]);
1516:                    }
1517:                    return result.toString();
1518:                }
1519:
1520:                DateTimeMatcher set(String pattern, FormatParser fp) {
1521:                    if (pattern.indexOf("\\u") >= 0) {
1522:                        String oldPattern = pattern;
1523:                        pattern = fromHex.transliterate(pattern);
1524:                    }
1525:                    for (int i = 0; i < TYPE_LIMIT; ++i) {
1526:                        type[i] = NONE;
1527:                        original[i] = "";
1528:                        baseOriginal[i] = "";
1529:                    }
1530:                    fp.set(pattern);
1531:                    for (Iterator it = fp.getVariableFields(new ArrayList())
1532:                            .iterator(); it.hasNext();) {
1533:                        String field = (String) it.next();
1534:                        if (field.charAt(0) == 'a')
1535:                            continue; // skip day period, special cass
1536:                        int canonicalIndex = getCanonicalIndex(field);
1537:                        if (canonicalIndex < 0) {
1538:                            throw new IllegalArgumentException(
1539:                                    "Illegal field:\t" + field + "\t in "
1540:                                            + pattern);
1541:                        }
1542:                        int[] row = types[canonicalIndex];
1543:                        int typeValue = row[1];
1544:                        if (original[typeValue].length() != 0) {
1545:                            throw new IllegalArgumentException(
1546:                                    "Conflicting fields:\t"
1547:                                            + original[typeValue] + ", "
1548:                                            + field + "\t in " + pattern);
1549:                        }
1550:                        original[typeValue] = field;
1551:                        char repeatChar = (char) row[0];
1552:                        int repeatCount = row[3];
1553:                        if (repeatCount > 3)
1554:                            repeatCount = 3; // hack to discard differences
1555:                        if ("GEzvQ".indexOf(repeatChar) >= 0)
1556:                            repeatCount = 1;
1557:                        baseOriginal[typeValue] = Utility.repeat(String
1558:                                .valueOf(repeatChar), repeatCount);
1559:                        int subTypeValue = row[2];
1560:                        if (subTypeValue > 0)
1561:                            subTypeValue += field.length();
1562:                        type[typeValue] = (byte) subTypeValue;
1563:                    }
1564:                    return this ;
1565:                }
1566:
1567:                /**
1568:                 * 
1569:                 */
1570:                int getFieldMask() {
1571:                    int result = 0;
1572:                    for (int i = 0; i < type.length; ++i) {
1573:                        if (type[i] != 0)
1574:                            result |= (1 << i);
1575:                    }
1576:                    return result;
1577:                }
1578:
1579:                /**
1580:                 * 
1581:                 */
1582:                void extractFrom(DateTimeMatcher source, int fieldMask) {
1583:                    for (int i = 0; i < type.length; ++i) {
1584:                        if ((fieldMask & (1 << i)) != 0) {
1585:                            type[i] = source.type[i];
1586:                            original[i] = source.original[i];
1587:                        } else {
1588:                            type[i] = NONE;
1589:                            original[i] = "";
1590:                        }
1591:                    }
1592:                }
1593:
1594:                int getDistance(DateTimeMatcher other, int includeMask,
1595:                        DistanceInfo distanceInfo) {
1596:                    int result = 0;
1597:                    distanceInfo.clear();
1598:                    for (int i = 0; i < type.length; ++i) {
1599:                        int myType = (includeMask & (1 << i)) == 0 ? 0
1600:                                : type[i];
1601:                        int otherType = other.type[i];
1602:                        if (myType == otherType)
1603:                            continue; // identical (maybe both zero) add 0
1604:                        if (myType == 0) { // and other is not
1605:                            result += EXTRA_FIELD;
1606:                            distanceInfo.addExtra(i);
1607:                        } else if (otherType == 0) { // and mine is not
1608:                            result += MISSING_FIELD;
1609:                            distanceInfo.addMissing(i);
1610:                        } else {
1611:                            result += Math.abs(myType - otherType); // square of mismatch
1612:                        }
1613:                    }
1614:                    return result;
1615:                }
1616:
1617:                public int compareTo(Object o) {
1618:                    DateTimeMatcher that = (DateTimeMatcher) o;
1619:                    for (int i = 0; i < original.length; ++i) {
1620:                        int comp = original[i].compareTo(that.original[i]);
1621:                        if (comp != 0)
1622:                            return -comp;
1623:                    }
1624:                    return 0;
1625:                }
1626:
1627:                public boolean equals(Object other) {
1628:                    if (other == null)
1629:                        return false;
1630:                    DateTimeMatcher that = (DateTimeMatcher) other;
1631:                    for (int i = 0; i < original.length; ++i) {
1632:                        if (!original[i].equals(that.original[i]))
1633:                            return false;
1634:                    }
1635:                    return true;
1636:                }
1637:
1638:                public int hashCode() {
1639:                    int result = 0;
1640:                    for (int i = 0; i < original.length; ++i) {
1641:                        result ^= original[i].hashCode();
1642:                    }
1643:                    return result;
1644:                }
1645:            }
1646:
1647:            private static class DistanceInfo {
1648:                int missingFieldMask;
1649:                int extraFieldMask;
1650:
1651:                void clear() {
1652:                    missingFieldMask = extraFieldMask = 0;
1653:                }
1654:
1655:                /**
1656:                 * 
1657:                 */
1658:                void setTo(DistanceInfo other) {
1659:                    missingFieldMask = other.missingFieldMask;
1660:                    extraFieldMask = other.extraFieldMask;
1661:                }
1662:
1663:                void addMissing(int field) {
1664:                    missingFieldMask |= (1 << field);
1665:                }
1666:
1667:                void addExtra(int field) {
1668:                    extraFieldMask |= (1 << field);
1669:                }
1670:
1671:                public String toString() {
1672:                    return "missingFieldMask: "
1673:                            + DateTimePatternGenerator
1674:                                    .showMask(missingFieldMask)
1675:                            + ", extraFieldMask: "
1676:                            + DateTimePatternGenerator.showMask(extraFieldMask);
1677:                }
1678:            }
1679:        }
1680:        //#endif
1681:        //eof
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.