1 //##header 1189099963000 FOUNDATION2 //#ifndef FOUNDATION3 //##/*4 //## *******************************************************************************5 //## * Copyright (C) 2006, Google, International Business Machines Corporation and *6 //## * others. All Rights Reserved. *7 //## *******************************************************************************8 //## *9 //## * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/text/DateTimePatternGenerator.java,v $10 //## * $Date: 2006/09/15 18:09:24 $11 //## * $Revision: 1.9 $12 //## *13 //## *******************************************************************************14 //## */15 //##package com.ibm.icu.text;16 //##17 //##import java.util.ArrayList;18 //##import java.util.Arrays;19 //##import java.util.BitSet;20 //##import java.util.Collection;21 //##import java.util.Collections;22 //##import java.util.Enumeration;23 //##import java.util.HashMap;24 //##import java.util.HashSet;25 //##import java.util.Iterator;26 //##import java.util.LinkedHashMap;27 //##import java.util.LinkedHashSet;28 //##import java.util.List;29 //##import java.util.Map;30 //##import java.util.Set;31 //##import java.util.TreeMap;32 //##import java.util.TreeSet;33 //##34 //##//import org.unicode.cldr.util.Utility;35 //##36 //##import com.ibm.icu.impl.CalendarData;37 //##import com.ibm.icu.impl.PatternTokenizer;38 //##import com.ibm.icu.impl.Utility;39 //##import com.ibm.icu.text.MessageFormat;40 //##import com.ibm.icu.text.Transliterator;41 //##import com.ibm.icu.text.UnicodeSet;42 //##import com.ibm.icu.util.Calendar;43 //##import com.ibm.icu.util.Freezable;44 //##import com.ibm.icu.util.ULocale;45 //##import com.ibm.icu.util.UResourceBundle;46 //##47 //##/**48 //## * This class provides flexible generation of date format patterns, like "yy-MM-dd". The user can build up the generator49 //## * by adding successive patterns. Once that is done, a query can be made using a "skeleton", which is a pattern which just50 //## * includes the desired fields and lengths. The generator will return the "best fit" pattern corresponding to that skeleton.51 //## * <p>The main method people will use is getBestPattern(String skeleton),52 //## * since normally this class is pre-built with data from a particular locale. However, generators can be built directly from other data as well.53 //## * <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.54 //## * That would be useful for getting the UI order of field elements.</i>55 //## * @draft ICU 3.656 //## * @provisional This API might change or be removed in a future release.57 //## */58 //##public class DateTimePatternGenerator implements Freezable, Cloneable {59 //## // debugging flags60 //## //static boolean SHOW_DISTANCE = false;61 //## // TODO add hack to fix months for CJK, as per bug 109962 //## // http://dev.icu-project.org/cgi-bin/locale-bugs/incoming?findid=109963 //## 64 //## /**65 //## * Create empty generator, to be constructed with add(...) etc.66 //## * @draft ICU 3.667 //## * @provisional This API might change or be removed in a future release.68 //## */69 //## public static DateTimePatternGenerator newInstance() {70 //## return new DateTimePatternGenerator();71 //## }72 //## 73 //## /**74 //## * Only for use by subclasses75 //## * @draft ICU 3.676 //## * @provisional This API might change or be removed in a future release.77 //## */78 //## protected DateTimePatternGenerator() { 79 //## }80 //## 81 //## /**82 //## * Construct a flexible generator according to data for a given locale.83 //## * @draft ICU 3.684 //## * @provisional This API might change or be removed in a future release.85 //## */86 //## public static DateTimePatternGenerator getInstance() {87 //## return getInstance(ULocale.getDefault());88 //## }89 //## 90 //## /**91 //## * Construct a flexible generator according to data for a given locale.92 //## * @param uLocale93 //## * @draft ICU 3.694 //## * @provisional This API might change or be removed in a future release.95 //## */96 //## public static DateTimePatternGenerator getInstance(ULocale uLocale) {97 //## DateTimePatternGenerator result = new DateTimePatternGenerator();98 //## PatternInfo returnInfo = new PatternInfo();99 //## String hackPattern = null;100 //## // first load with the ICU patterns101 //## for (int i = DateFormat.FULL; i <= DateFormat.SHORT; ++i) {102 //## SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateInstance(i, uLocale);103 //## result.add(df.toPattern(), false, returnInfo);104 //## df = (SimpleDateFormat) DateFormat.getTimeInstance(i, uLocale);105 //## result.add(df.toPattern(), false, returnInfo);106 //## // HACK for hh:ss107 //## if (i == DateFormat.MEDIUM) {108 //## hackPattern = df.toPattern();109 //## }110 //## }111 //## UResourceBundle rb = UResourceBundle.getBundleInstance("com.ibm.icu.impl.data.DateData$MyDateResources", uLocale);112 //## //ResourceBundle rb = ResourceBundle.getBundle("com.ibm.icu.impl.data.DateData$MyDateResources", ULocale.FRENCH.toLocale());113 //## for (Enumeration en = rb.getKeys(); en.hasMoreElements();) {114 //## String key = (String) en.nextElement();115 //## String value = rb.getString(key);116 //## String [] keyParts = key.split("/");117 //## if (keyParts[0].equals("pattern")) {118 //## result.add(value, false, returnInfo);119 //## } else if (keyParts[0].equals("append")) {120 //## result.setAppendItemFormats(getAppendFormatNumber(keyParts[1]), value);121 //## } else if (keyParts[0].equals("field")) {122 //## result.setAppendItemNames(getAppendNameNumber(keyParts[1]), value);123 //## }124 //## }125 //## 126 //## // assume it is always big endian (ok for CLDR right now)127 //## // some languages didn't add mm:ss or HH:mm, so put in a hack to compute that from the short time.128 //## if (hackPattern != null) {129 //## hackTimes(result, returnInfo, hackPattern);130 //## }131 //## 132 //## // set the datetime pattern. This is ugly code -- there should be a public interface for this133 //## Calendar cal = Calendar.getInstance(uLocale);134 //## CalendarData calData = new CalendarData(uLocale, cal.getType());135 //## String[] patterns = calData.get("DateTimePatterns").getStringArray();136 //## result.setDateTimeFormat(patterns[8]);137 //## 138 //## // decimal point for seconds139 //## DecimalFormatSymbols dfs = new DecimalFormatSymbols(uLocale);140 //## result.setDecimal(String.valueOf(dfs.getDecimalSeparator()));141 //## return result;142 //## }143 //## 144 //## private static void hackTimes(DateTimePatternGenerator result, PatternInfo returnInfo, String hackPattern) {145 //## result.fp.set(hackPattern);146 //## String mmss = new String();147 //## // to get mm:ss, we strip all but mm literal ss148 //## boolean gotMm = false;149 //## for (int i = 0; i < result.fp.items.size(); ++i) {150 //## Object item = result.fp.items.get(i);151 //## if (item instanceof String) {152 //## if (gotMm) {153 //## mmss += result.fp.quoteLiteral(item.toString());154 //## }155 //## } else {156 //## char ch = item.toString().charAt(0);157 //## if (ch == 'm') {158 //## gotMm = true;159 //## mmss += item;160 //## } else if (ch == 's') {161 //## if (!gotMm) {162 //## break; // failed163 //## }164 //## mmss += item;165 //## result.add(mmss, false, returnInfo);166 //## break;167 //## } else if (gotMm || ch == 'z' || ch == 'Z' || ch == 'v' || ch == 'V') {168 //## break; // failed169 //## }170 //## }171 //## }172 //## // to get hh:mm, we strip (literal ss) and (literal S)173 //## // the easiest way to do this is to mark the stuff we want to nuke, then remove it in a second pass.174 //## BitSet variables = new BitSet();175 //## BitSet nuke = new BitSet();176 //## for (int i = 0; i < result.fp.items.size(); ++i) {177 //## Object item = result.fp.items.get(i);178 //## if (item instanceof VariableField) {179 //## variables.set(i);180 //## char ch = item.toString().charAt(0);181 //## if (ch == 's' || ch == 'S') {182 //## nuke.set(i);183 //## for (int j = i-1; j >= 0; ++j) {184 //## if (variables.get(j)) break;185 //## nuke.set(i);186 //## }187 //## }188 //## }189 //## }190 //## String hhmm = getFilteredPattern(result.fp, nuke);191 //## result.add(hhmm, false, returnInfo);192 //## }193 //## 194 //## private static String getFilteredPattern(FormatParser fp, BitSet nuke) {195 //## String result = new String();196 //## for (int i = 0; i < fp.items.size(); ++i) {197 //## if (nuke.get(i)) continue;198 //## Object item = fp.items.get(i);199 //## if (item instanceof String) {200 //## result += fp.quoteLiteral(item.toString());201 //## } else {202 //## result += item.toString();203 //## }204 //## }205 //## return result;206 //## }207 //## 208 //## private static int getAppendNameNumber(String string) {209 //## for (int i = 0; i < CLDR_FIELD_NAME.length; ++i) {210 //## if (CLDR_FIELD_NAME[i].equals(string)) return i;211 //## }212 //## return -1;213 //## }214 //## 215 //## private static int getAppendFormatNumber(String string) {216 //## for (int i = 0; i < CLDR_FIELD_APPEND.length; ++i) {217 //## if (CLDR_FIELD_APPEND[i].equals(string)) return i;218 //## }219 //## return -1;220 //## 221 //## }222 //## 223 //## /**224 //## * Return the best pattern matching the input skeleton. It is guaranteed to225 //## * have all of the fields in the skeleton.226 //## * 227 //## * @param skeleton228 //## * The skeleton is a pattern containing only the variable fields.229 //## * For example, "MMMdd" and "mmhh" are skeletons.230 //## * @draft ICU 3.6231 //## * @provisional This API might change or be removed in a future release.232 //## */233 //## public String getBestPattern(String skeleton) {234 //## //if (!isComplete) complete();235 //## current.set(skeleton, fp);236 //## String best = getBestRaw(current, -1, _distanceInfo);237 //## if (_distanceInfo.missingFieldMask == 0 && _distanceInfo.extraFieldMask == 0) {238 //## // we have a good item. Adjust the field types239 //## return adjustFieldTypes(best, current, false);240 //## }241 //## int neededFields = current.getFieldMask();242 //## // otherwise break up by date and time.243 //## String datePattern = getBestAppending(neededFields & DATE_MASK);244 //## String timePattern = getBestAppending(neededFields & TIME_MASK);245 //## 246 //## if (datePattern == null) return timePattern == null ? "" : timePattern;247 //## if (timePattern == null) return datePattern;248 //## return MessageFormat.format(getDateTimeFormat(), new Object[]{datePattern, timePattern});249 //## }250 //## 251 //## /**252 //## * PatternInfo supplies output parameters for add(...). It is used because253 //## * Java doesn't have real output parameters. It is treated like a struct (eg254 //## * Point), so all fields are public.255 //## * 256 //## * @draft ICU 3.6257 //## * @provisional This API might change or be removed in a future release.258 //## */259 //## public static final class PatternInfo { // struct for return information260 //## /**261 //## * @draft ICU 3.6262 //## * @provisional This API might change or be removed in a future release.263 //## */264 //## public static final int OK = 0;265 //## 266 //## /**267 //## * @draft ICU 3.6268 //## * @provisional This API might change or be removed in a future release.269 //## */270 //## public static final int BASE_CONFLICT = 1;271 //## 272 //## /**273 //## * @draft ICU 3.6274 //## * @provisional This API might change or be removed in a future release.275 //## */276 //## public static final int CONFLICT = 2;277 //## 278 //## /**279 //## * @draft ICU 3.6280 //## * @provisional This API might change or be removed in a future release.281 //## */282 //## public int status;283 //## 284 //## /**285 //## * @draft ICU 3.6286 //## * @provisional This API might change or be removed in a future release.287 //## */288 //## public String conflictingPattern;289 //## 290 //## /**291 //## * Simple constructor, since this is treated like a struct.292 //## * @draft ICU 3.6293 //## * @provisional This API might change or be removed in a future release.294 //## */295 //## public PatternInfo() {296 //## }297 //## }298 //## 299 //## static Transliterator fromHex = Transliterator.getInstance("hex-any");300 //## 301 //## /**302 //## * Adds a pattern to the generator. If the pattern has the same skeleton as303 //## * an existing pattern, and the override parameter is set, then the previous304 //## * value is overriden. Otherwise, the previous value is retained. In either305 //## * case, the conflicting information is returned in PatternInfo.306 //## * <p>307 //## * Note that single-field patterns (like "MMM") are automatically added, and308 //## * don't need to be added explicitly!309 //## * 310 //## * @param override311 //## * when existing values are to be overridden use true, otherwise312 //## * use false.313 //## * @draft ICU 3.6314 //## * @provisional This API might change or be removed in a future release.315 //## */316 //## public DateTimePatternGenerator add(String pattern, boolean override, PatternInfo returnInfo) {317 //## checkFrozen();318 //## if (pattern.indexOf("\\u") >= 0) {319 //## String oldPattern = pattern;320 //## pattern = fromHex.transliterate(pattern);321 //## }322 //## DateTimeMatcher matcher = new DateTimeMatcher().set(pattern, fp);323 //## String basePattern = matcher.getBasePattern();324 //## String previousPatternWithSameBase = (String)basePattern_pattern.get(basePattern);325 //## if (previousPatternWithSameBase != null) {326 //## returnInfo.status = PatternInfo.BASE_CONFLICT;327 //## returnInfo.conflictingPattern = previousPatternWithSameBase;328 //## if (!override) return this;329 //## }330 //## String previousValue = (String)skeleton2pattern.get(matcher);331 //## if (previousValue != null) {332 //## returnInfo.status = PatternInfo.CONFLICT;333 //## returnInfo.conflictingPattern = previousValue;334 //## if (!override) return this;335 //## }336 //## returnInfo.status = PatternInfo.OK;337 //## returnInfo.conflictingPattern = "";338 //## skeleton2pattern.put(matcher, pattern);339 //## basePattern_pattern.put(basePattern, pattern);340 //## return this;341 //## }342 //## 343 //## /**344 //## * Utility to return a unique skeleton from a given pattern. For example,345 //## * both "MMM-dd" and "dd/MMM" produce the skeleton "MMMdd".346 //## * 347 //## * @param pattern348 //## * Input pattern, such as "dd/MMM"349 //## * @return skeleton, such as "MMMdd"350 //## * @draft ICU 3.6351 //## * @provisional This API might change or be removed in a future release.352 //## */353 //## public String getSkeleton(String pattern) {354 //## synchronized (this) { // synchronized since a getter must be thread-safe355 //## current.set(pattern, fp);356 //## return current.toString();357 //## }358 //## }359 //## 360 //## /**361 //## * Utility to return a unique base skeleton from a given pattern. This is362 //## * the same as the skeleton, except that differences in length are minimized363 //## * so as to only preserve the difference between string and numeric form. So364 //## * for example, both "MMM-dd" and "d/MMM" produce the skeleton "MMMd"365 //## * (notice the single d).366 //## * 367 //## * @param pattern368 //## * Input pattern, such as "dd/MMM"369 //## * @return skeleton, such as "MMMdd"370 //## * @draft ICU 3.6371 //## * @provisional This API might change or be removed in a future release.372 //## */373 //## public String getBaseSkeleton(String pattern) {374 //## synchronized (this) { // synchronized since a getter must be thread-safe375 //## current.set(pattern, fp);376 //## return current.getBasePattern();377 //## }378 //## }379 //## 380 //## /**381 //## * Return a list of all the skeletons (in canonical form) from this class,382 //## * and the patterns that they map to.383 //## * 384 //## * @param result385 //## * an output Map in which to place the mapping from skeleton to386 //## * pattern. If you want to see the internal order being used,387 //## * supply a LinkedHashMap. If the input value is null, then a388 //## * LinkedHashMap is allocated.389 //## * <p>390 //## * <i>Issue: an alternate API would be to just return a list of391 //## * the skeletons, and then have a separate routine to get from392 //## * skeleton to pattern.</i>393 //## * @return the input Map containing the values.394 //## * @draft ICU 3.6395 //## * @provisional This API might change or be removed in a future release.396 //## */397 //## public Map getSkeletons(Map result) {398 //## if (result == null) result = new LinkedHashMap();399 //## for (Iterator it = skeleton2pattern.keySet().iterator(); it.hasNext();) {400 //## DateTimeMatcher item = (DateTimeMatcher) it.next();401 //## String pattern = (String) skeleton2pattern.get(item);402 //## if (CANONICAL_SET.contains(pattern)) continue;403 //## result.put(item.toString(), pattern);404 //## }405 //## return result;406 //## }407 //## 408 //## /**409 //## * Return a list of all the base skeletons (in canonical form) from this class410 //## * @draft ICU 3.6411 //## * @provisional This API might change or be removed in a future release.412 //## */413 //## public Set getBaseSkeletons(Set result) {414 //## if (result == null) result = new HashSet();415 //## result.addAll(basePattern_pattern.keySet());416 //## return result;417 //## }418 //## 419 //## /**420 //## * Adjusts the field types (width and subtype) of a pattern to match what is421 //## * in a skeleton. That is, if you supply a pattern like "d-M H:m", and a422 //## * skeleton of "MMMMddhhmm", then the input pattern is adjusted to be423 //## * "dd-MMMM hh:mm". This is used internally to get the best match for the424 //## * input skeleton, but can also be used externally.425 //## * 426 //## * @param pattern427 //## * input pattern428 //## * @param skeleton429 //## * @return pattern adjusted to match the skeleton fields widths and430 //## * subtypes.431 //## * @draft ICU 3.6432 //## * @provisional This API might change or be removed in a future release.433 //## */434 //## public String replaceFieldTypes(String pattern, String skeleton) {435 //## synchronized (this) { // synchronized since a getter must be thread-safe436 //## return adjustFieldTypes(pattern, current.set(skeleton, fp), false);437 //## }438 //## }439 //## 440 //## /**441 //## * The date time format is a message format pattern used to compose date and442 //## * time patterns. The default value is "{0} {1}", where {0} will be replaced443 //## * by the date pattern and {1} will be replaced by the time pattern.444 //## * <p>445 //## * This is used when the input skeleton contains both date and time fields,446 //## * but there is not a close match among the added patterns. For example,447 //## * suppose that this object was created by adding "dd-MMM" and "hh:mm", and448 //## * its datetimeFormat is the default "{0} {1}". Then if the input skeleton449 //## * is "MMMdhmm", there is not an exact match, so the input skeleton is450 //## * broken up into two components "MMMd" and "hmm". There are close matches451 //## * for those two skeletons, so the result is put together with this pattern,452 //## * resulting in "d-MMM h:mm".453 //## * 454 //## * @param dateTimeFormat455 //## * message format pattern, here {0} will be replaced by the date456 //## * pattern and {1} will be replaced by the time pattern.457 //## * @draft ICU 3.6458 //## * @provisional This API might change or be removed in a future release.459 //## */460 //## public void setDateTimeFormat(String dateTimeFormat) {461 //## checkFrozen();462 //## this.dateTimeFormat = dateTimeFormat;463 //## }464 //## 465 //## /**466 //## * Getter corresponding to setDateTimeFormat.467 //## * 468 //## * @return pattern469 //## * @draft ICU 3.6470 //## * @provisional This API might change or be removed in a future release.471 //## */472 //## public String getDateTimeFormat() {473 //## return dateTimeFormat;474 //## }475 //## 476 //## /**477 //## * The decimal value is used in formatting fractions of seconds. If the478 //## * skeleton contains fractional seconds, then this is used with the479 //## * fractional seconds. For example, suppose that the input pattern is480 //## * "hhmmssSSSS", and the best matching pattern internally is "H:mm:ss", and481 //## * the decimal string is ",". Then the resulting pattern is modified to be482 //## * "H:mm:ss,SSSS"483 //## * 484 //## * @param decimal485 //## * @draft ICU 3.6486 //## * @provisional This API might change or be removed in a future release.487 //## */488 //## public void setDecimal(String decimal) {489 //## checkFrozen();490 //## this.decimal = decimal;491 //## }492 //## 493 //## /**494 //## * Getter corresponding to setDecimal.495 //## * @return string corresponding to the decimal point496 //## * @draft ICU 3.6497 //## * @provisional This API might change or be removed in a future release.498 //## */499 //## public String getDecimal() {500 //## return decimal;501 //## }502 //## 503 //## /**504 //## * Redundant patterns are those which if removed, make no difference in the505 //## * resulting getBestPattern values. This method returns a list of them, to506 //## * help check the consistency of the patterns used to build this generator.507 //## * 508 //## * @param output509 //## * stores the redundant patterns that are removed. To get these510 //## * in internal order, supply a LinkedHashSet. If null, a511 //## * collection is allocated.512 //## * @return the collection with added elements.513 //## * @deprecated514 //## * @internal515 //## */516 //## public Collection getRedundants(Collection output) {517 //## synchronized (this) { // synchronized since a getter must be thread-safe518 //## if (output == null) output = new LinkedHashSet();519 //## for (Iterator it = skeleton2pattern.keySet().iterator(); it.hasNext();) {520 //## DateTimeMatcher current = (DateTimeMatcher) it.next();521 //## String pattern = (String) skeleton2pattern.get(current);522 //## if (CANONICAL_SET.contains(pattern)) continue;523 //## skipMatcher = current;524 //## String trial = getBestPattern(current.toString());525 //## if (trial.equals(pattern)) {526 //## output.add(pattern);527 //## }528 //## }529 //## if (false) { // ordered530 //## DateTimePatternGenerator results = new DateTimePatternGenerator();531 //## PatternInfo pinfo = new PatternInfo();532 //## for (Iterator it = skeleton2pattern.keySet().iterator(); it.hasNext();) {533 //## DateTimeMatcher current = (DateTimeMatcher) it.next();534 //## String pattern = (String) skeleton2pattern.get(current);535 //## if (CANONICAL_SET.contains(pattern)) continue;536 //## //skipMatcher = current;537 //## String trial = results.getBestPattern(current.toString());538 //## if (trial.equals(pattern)) {539 //## output.add(pattern);540 //## } else {541 //## results.add(pattern, false, pinfo);542 //## }543 //## }544 //## }545 //## return output;546 //## }547 //## }548 //## 549 //## // Field numbers, used for AppendItem functions550 //## 551 //## /** 552 //## * @draft ICU 3.6553 //## * @provisional This API might change or be removed in a future release.554 //## */555 //## static final public int ERA = 0;556 //## 557 //## /**558 //## * @draft ICU 3.6559 //## * @provisional This API might change or be removed in a future release.560 //## */561 //## static final public int YEAR = 1; 562 //## 563 //## /**564 //## * @draft ICU 3.6565 //## * @provisional This API might change or be removed in a future release.566 //## */567 //## static final public int QUARTER = 2; 568 //## 569 //## /**570 //## * @draft ICU 3.6571 //## * @provisional This API might change or be removed in a future release.572 //## */573 //## static final public int MONTH = 3;574 //## 575 //## /**576 //## * @draft ICU 3.6577 //## * @provisional This API might change or be removed in a future release.578 //## */579 //## static final public int WEEK_OF_YEAR = 4; 580 //## 581 //## /**582 //## * @draft ICU 3.6583 //## * @provisional This API might change or be removed in a future release.584 //## */585 //## static final public int WEEK_OF_MONTH = 5; 586 //## 587 //## /**588 //## * @draft ICU 3.6589 //## * @provisional This API might change or be removed in a future release.590 //## */591 //## static final public int WEEKDAY = 6; 592 //## 593 //## /**594 //## * @draft ICU 3.6595 //## * @provisional This API might change or be removed in a future release.596 //## */597 //## static final public int DAY = 7;598 //## 599 //## /**600 //## * @draft ICU 3.6601 //## * @provisional This API might change or be removed in a future release.602 //## */603 //## static final public int DAY_OF_YEAR = 8; 604 //## 605 //## /**606 //## * @draft ICU 3.6607 //## * @provisional This API might change or be removed in a future release.608 //## */609 //## static final public int DAY_OF_WEEK_IN_MONTH = 9; 610 //## 611 //## /**612 //## * @draft ICU 3.6613 //## * @provisional This API might change or be removed in a future release.614 //## */615 //## static final public int DAYPERIOD = 10;616 //## 617 //## /**618 //## * @draft ICU 3.6619 //## * @provisional This API might change or be removed in a future release.620 //## */621 //## static final public int HOUR = 11; 622 //## 623 //## /**624 //## * @draft ICU 3.6625 //## * @provisional This API might change or be removed in a future release.626 //## */627 //## static final public int MINUTE = 12; 628 //## 629 //## /**630 //## * @draft ICU 3.6631 //## * @provisional This API might change or be removed in a future release.632 //## */633 //## static final public int SECOND = 13; 634 //## 635 //## /**636 //## * @draft ICU 3.6637 //## * @provisional This API might change or be removed in a future release.638 //## */639 //## static final public int FRACTIONAL_SECOND = 14;640 //## 641 //## /**642 //## * @draft ICU 3.6643 //## * @provisional This API might change or be removed in a future release.644 //## */645 //## static final public int ZONE = 15; 646 //## 647 //## /**648 //## * @draft ICU 3.6649 //## * @provisional This API might change or be removed in a future release.650 //## */651 //## static final public int TYPE_LIMIT = 16;652 //## 653 //## /**654 //## * An AppendItem format is a pattern used to append a field if there is no655 //## * good match. For example, suppose that the input skeleton is "GyyyyMMMd",656 //## * and there is no matching pattern internally, but there is a pattern657 //## * matching "yyyyMMMd", say "d-MM-yyyy". Then that pattern is used, plus the658 //## * G. The way these two are conjoined is by using the AppendItemFormat for G659 //## * (era). So if that value is, say "{0}, {1}" then the final resulting660 //## * pattern is "d-MM-yyyy, G".661 //## * <p>662 //## * There are actually three available variables: {0} is the pattern so far,663 //## * {1} is the element we are adding, and {2} is the name of the element.664 //## * <p>665 //## * This reflects the way that the CLDR data is organized.666 //## * 667 //## * @param field668 //## * such as ERA669 //## * @param value670 //## * pattern, such as "{0}, {1}"671 //## * @draft ICU 3.6672 //## * @provisional This API might change or be removed in a future release.673 //## */674 //## public void setAppendItemFormats(int field, String value) {675 //## checkFrozen();676 //## appendItemFormats[field] = value;677 //## }678 //## 679 //## /**680 //## * Getter corresponding to setAppendItemFormats. Values below 0 or at or681 //## * above TYPE_LIMIT are illegal arguments.682 //## * 683 //## * @param field684 //## * @return append pattern for field685 //## * @draft ICU 3.6686 //## * @provisional This API might change or be removed in a future release.687 //## */688 //## public String getAppendItemFormats(int field) {689 //## return appendItemFormats[field];690 //## }691 //## 692 //## /**693 //## * Sets the names of fields, eg "era" in English for ERA. These are only694 //## * used if the corresponding AppendItemFormat is used, and if it contains a695 //## * {2} variable.696 //## * <p>697 //## * This reflects the way that the CLDR data is organized.698 //## * 699 //## * @param field700 //## * @param value701 //## * @draft ICU 3.6702 //## * @provisional This API might change or be removed in a future release.703 //## */704 //## public void setAppendItemNames(int field, String value) {705 //## checkFrozen();706 //## appendItemNames[field] = value;707 //## }708 //## 709 //## /**710 //## * Getter corresponding to setAppendItemNames. Values below 0 or at or above711 //## * TYPE_LIMIT are illegal arguments.712 //## * 713 //## * @param field714 //## * @return name for field715 //## * @draft ICU 3.6716 //## * @provisional This API might change or be removed in a future release.717 //## */718 //## public String getAppendItemNames(int field) {719 //## return appendItemNames[field];720 //## }721 //## 722 //## /**723 //## * Determines whether a skeleton contains a single field724 //## * 725 //## * @param skeleton726 //## * @return true or not727 //## * @deprecated728 //## * @internal729 //## */730 //## public static boolean isSingleField(String skeleton) {731 //## char first = skeleton.charAt(0);732 //## for (int i = 1; i < skeleton.length(); ++i) {733 //## if (skeleton.charAt(i) != first) return false;734 //## }735 //## return true;736 //## }737 //## 738 //## /**739 //## * Boilerplate for Freezable740 //## * @draft ICU 3.6741 //## * @provisional This API might change or be removed in a future release.742 //## */743 //## public boolean isFrozen() {744 //## return frozen;745 //## }746 //## 747 //## /**748 //## * Boilerplate for Freezable749 //## * @draft ICU 3.6750 //## * @provisional This API might change or be removed in a future release.751 //## */752 //## public Object freeze() {753 //## frozen = true;754 //## return this;755 //## }756 //## 757 //## /**758 //## * Boilerplate for Freezable759 //## * @draft ICU 3.6760 //## * @provisional This API might change or be removed in a future release.761 //## */762 //## public Object cloneAsThawed() {763 //## DateTimePatternGenerator result = (DateTimePatternGenerator) (this.clone());764 //## frozen = false;765 //## return result;766 //## }767 //## 768 //## /**769 //## * Boilerplate770 //## * @draft ICU 3.6771 //## * @provisional This API might change or be removed in a future release.772 //## */773 //## public Object clone() {774 //## try {775 //## DateTimePatternGenerator result = (DateTimePatternGenerator) (super.clone());776 //## result.skeleton2pattern = (TreeMap) skeleton2pattern.clone();777 //## result.basePattern_pattern = (TreeMap) basePattern_pattern.clone();778 //## result.appendItemFormats = (String[]) appendItemFormats.clone();779 //## result.appendItemNames = (String[]) appendItemNames.clone();780 //## result.current = new DateTimeMatcher();781 //## result.fp = new FormatParser();782 //## result._distanceInfo = new DistanceInfo();783 //## 784 //## result.frozen = false;785 //## return result;786 //## } catch (CloneNotSupportedException e) {787 //## throw new IllegalArgumentException("Internal Error");788 //## }789 //## }790 //## 791 //## /**792 //## * Utility class for FormatParser. Immutable class.793 //## * @deprecated794 //## * @internal795 //## */796 //## public static class VariableField {797 //## private String string;798 //## /**799 //## * Create a variable field800 //## * @param string801 //## * @deprecated802 //## * @internal803 //## */804 //## public VariableField(String string) {805 //## this.string = string;806 //## }807 //## /**808 //## * Get the internal results809 //## * @deprecated810 //## * @internal811 //## */812 //## public String toString() {813 //## return string;814 //## }815 //## }816 //## 817 //## /**818 //## * Class providing date formatting819 //## * @deprecated820 //## * @internal821 //## */822 //## static public class FormatParser {823 //## private transient PatternTokenizer tokenizer = new PatternTokenizer()824 //## .setSyntaxCharacters(new UnicodeSet("[a-zA-Z]"))825 //## //.setEscapeCharacters(new UnicodeSet("[^\\u0020-\\u007E]")) // WARNING: DateFormat doesn't accept \\uXXXX826 //## .setUsingQuote(true);827 //## private List items = new ArrayList();828 //## 829 //## /**830 //## * Set the string to parse831 //## * @param string832 //## * @return this, for chaining833 //## * @deprecated834 //## * @internal835 //## */836 //## public FormatParser set(String string) {837 //## items.clear();838 //## if (string.length() == 0) return this;839 //## tokenizer.setPattern(string);840 //## StringBuffer buffer = new StringBuffer();841 //## StringBuffer variable = new StringBuffer();842 //## while (true) {843 //## buffer.setLength(0);844 //## int status = tokenizer.next(buffer);845 //## if (status == PatternTokenizer.DONE) break;846 //## if (status == PatternTokenizer.SYNTAX) {847 //## if (variable.length() != 0 && buffer.charAt(0) != variable.charAt(0)) {848 //## addVariable(variable);849 //## }850 //## variable.append(buffer);851 //## } else {852 //## addVariable(variable);853 //## items.add(buffer.toString());854 //## }855 //## }856 //## addVariable(variable);857 //## return this;858 //## }859 //## 860 //## private void addVariable(StringBuffer variable) {861 //## if (variable.length() != 0) {862 //## items.add(new VariableField(variable.toString()));863 //## variable.setLength(0);864 //## }865 //## }866 //## 867 //## /** Return a collection of fields. These will be a mixture of Strings and VariableFields. Any "a" variable field is removed.868 //## * @param output List to append the items to. If null, is allocated as an ArrayList.869 //## * @return list870 //## */871 //## private List getVariableFields(List output) {872 //## if (output == null) output = new ArrayList();873 //## main:874 //## for (Iterator it = items.iterator(); it.hasNext();) {875 //## Object item = it.next();876 //## if (item instanceof VariableField) {877 //## String s = item.toString();878 //## switch(s.charAt(0)) {879 //## //case 'Q': continue main; // HACK880 //## case 'a': continue main; // remove881 //## }882 //## output.add(s);883 //## }884 //## }885 //## //System.out.println(output);886 //## return output;887 //## }888 //## 889 //## /**890 //## * @return a string which is a concatenation of all the variable fields891 //## * @deprecated892 //## * @internal893 //## */894 //## public String getVariableFieldString() {895 //## List list = getVariableFields(null);896 //## StringBuffer result = new StringBuffer();897 //## for (Iterator it = list.iterator(); it.hasNext();) {898 //## String item = (String) it.next();899 //## result.append(item);900 //## }901 //## return result.toString();902 //## }903 //## 904 //## /**905 //## * Returns modifiable list which is a mixture of Strings and VariableFields, in the order found during parsing.906 //## * @return modifiable list of items.907 //## * @deprecated908 //## * @internal909 //## */910 //## public List getItems() {911 //## return items;912 //## }913 //## 914 //## /** Provide display form of formatted input915 //## * @return printable output string916 //## * @deprecated917 //## * @internal918 //## */919 //## public String toString() {920 //## return toString(0, items.size());921 //## }922 //## 923 //## /**924 //## * Provide display form of formatted input925 //## * @param start item to start from926 //## * @param limit last item +1927 //## * @return printable output string928 //## * @deprecated929 //## * @internal930 //## */931 //## public String toString(int start, int limit) {932 //## StringBuffer result = new StringBuffer();933 //## for (int i = start; i < limit; ++i) {934 //## result.append(items.get(i).toString());935 //## }936 //## return result.toString();937 //## }938 //## 939 //## /**940 //## * Internal method <p>941 //## * Returns true if it has a mixture of date and time fields942 //## * @return true or false943 //## * @deprecated944 //## * @internal945 //## */946 //## public boolean hasDateAndTimeFields() {947 //## int foundMask = 0;948 //## for (Iterator it = items.iterator(); it.hasNext();) {949 //## Object item = it.next();950 //## if (item instanceof VariableField) {951 //## int type = getType(item);952 //## foundMask |= 1 << type; 953 //## }954 //## }955 //## boolean isDate = (foundMask & DATE_MASK) != 0;956 //## boolean isTime = (foundMask & TIME_MASK) != 0;957 //## return isDate && isTime;958 //## }959 //## 960 //## /**961 //## * Internal routine962 //## * @param value963 //## * @param result964 //## * @return list965 //## * @deprecated966 //## * @internal967 //## */968 //## public List getAutoPatterns(String value, List result) {969 //## if (result == null) result = new ArrayList();970 //## int fieldCount = 0;971 //## int minField = Integer.MAX_VALUE;972 //## int maxField = Integer.MIN_VALUE;973 //## for (Iterator it = items.iterator(); it.hasNext();) {974 //## Object item = it.next();975 //## if (item instanceof VariableField) {976 //## try {977 //## int type = getType(item);978 //## if (minField > type) minField = type;979 //## if (maxField < type) maxField = type;980 //## if (type == ZONE || type == DAYPERIOD || type == WEEKDAY) return result; // skip anything with zones 981 //## fieldCount++;982 //## } catch (Exception e) {983 //## return result; // if there are any funny fields, return984 //## }985 //## }986 //## }987 //## if (fieldCount < 3) return result; // skip988 //## // trim from start989 //## // trim first field IF there are no letters around it990 //## // and it is either the min or the max field991 //## // first field is either 0 or 1992 //## for (int i = 0; i < items.size(); ++i) {993 //## Object item = items.get(i);994 //## if (item instanceof VariableField) {995 //## int type = getType(item);996 //## if (type != minField && type != maxField) break;997 //## 998 //## if (i > 0) {999 //## Object previousItem = items.get(0);1000//## if (alpha.containsSome(previousItem.toString())) break;1001//## }1002//## int start = i+1;1003//## if (start < items.size()) {1004//## Object nextItem = items.get(start);1005//## if (nextItem instanceof String) {1006//## if (alpha.containsSome(nextItem.toString())) break;1007//## start++; // otherwise skip over string1008//## }1009//## }1010//## result.add(toString(start, items.size()));1011//## break;1012//## }1013//## }1014//## // now trim from end1015//## for (int i = items.size()-1; i >= 0; --i) {1016//## Object item = items.get(i);1017//## if (item instanceof VariableField) {1018//## int type = getType(item);1019//## if (type != minField && type != maxField) break;1020//## if (i < items.size() - 1) {1021//## Object previousItem = items.get(items.size() - 1);1022//## if (alpha.containsSome(previousItem.toString())) break;1023//## }1024//## int end = i-1;1025//## if (end > 0) {1026//## Object nextItem = items.get(end);1027//## if (nextItem instanceof String) {1028//## if (alpha.containsSome(nextItem.toString())) break;1029//## end--; // otherwise skip over string1030//## }1031//## }1032//## result.add(toString(0, end+1));1033//## break;1034//## }1035//## }1036//## 1037//## return result;1038//## }1039//## 1040//## private static UnicodeSet alpha = new UnicodeSet("[:alphabetic:]");1041//## 1042//## private int getType(Object item) {1043//## String s = item.toString();1044//## int canonicalIndex = getCanonicalIndex(s);1045//## if (canonicalIndex < 0) {1046//## throw new IllegalArgumentException("Illegal field:\t"1047//## + s);1048//## }1049//## int type = types[canonicalIndex][1];1050//## return type;1051//## }1052//## 1053//## /**1054//## * produce a quoted literal1055//## * @param string1056//## * @return string with quoted literals1057//## * @deprecated1058//## * @internal1059//## */1060//## public Object quoteLiteral(String string) {1061//## return tokenizer.quoteLiteral(string);1062//## }1063//## 1064//## /**1065//## * Simple constructor, since this is treated like a struct.1066//## * @deprecated1067//## * @internal1068//## */1069//## public FormatParser() {1070//## super();1071//## // TODO Auto-generated constructor stub1072//## }1073//## }1074//## // ========= PRIVATES ============1075//## 1076//## private TreeMap skeleton2pattern = new TreeMap(); // items are in priority order1077//## private TreeMap basePattern_pattern = new TreeMap(); // items are in priority order1078//## private String decimal = "?";1079//## private String dateTimeFormat = "{0} {1}";1080//## private String[] appendItemFormats = new String[TYPE_LIMIT];1081//## private String[] appendItemNames = new String[TYPE_LIMIT];1082//## {1083//## for (int i = 0; i < TYPE_LIMIT; ++i) {1084//## appendItemFormats[i] = "{0} \u251C{2}: {1}\u2524";1085//## appendItemNames[i] = "F" + i;1086//## }1087//## }1088//## 1089//## private transient DateTimeMatcher current = new DateTimeMatcher();1090//## private transient FormatParser fp = new FormatParser();1091//## private transient DistanceInfo _distanceInfo = new DistanceInfo();1092//## private transient boolean isComplete = false;1093//## private transient DateTimeMatcher skipMatcher = null; // only used temporarily, for internal purposes1094//## private transient boolean frozen = false;1095//## 1096//## private static final int FRACTIONAL_MASK = 1<<FRACTIONAL_SECOND;1097//## private static final int SECOND_AND_FRACTIONAL_MASK = (1<<SECOND) | (1<<FRACTIONAL_SECOND);1098//## 1099//## private void checkFrozen() {1100//## if (isFrozen()) {1101//## throw new UnsupportedOperationException("Attempt to modify frozen object");1102//## }1103//## }1104//## 1105//## /**1106//## * 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.1107//## * If we fail to find a complete skeleton, we compose in a loop until we have all the fields.1108//## */1109//## private String getBestAppending(int missingFields) {1110//## String resultPattern = null;1111//## if (missingFields != 0) {1112//## resultPattern = getBestRaw(current, missingFields, _distanceInfo);1113//## resultPattern = adjustFieldTypes(resultPattern, current, false);1114//## 1115//## while (_distanceInfo.missingFieldMask != 0) { // precondition: EVERY single field must work!1116//## 1117//## // special hack for SSS. If we are missing SSS, and we had ss but found it, replace the s field according to the 1118//## // number separator1119//## if ((_distanceInfo.missingFieldMask & SECOND_AND_FRACTIONAL_MASK) == FRACTIONAL_MASK1120//## && (missingFields & SECOND_AND_FRACTIONAL_MASK) == SECOND_AND_FRACTIONAL_MASK) {1121//## resultPattern = adjustFieldTypes(resultPattern, current, true);1122//## _distanceInfo.missingFieldMask &= ~FRACTIONAL_MASK; // remove bit1123//## continue;1124//## }1125//## 1126//## int startingMask = _distanceInfo.missingFieldMask;1127//## String temp = getBestRaw(current, _distanceInfo.missingFieldMask, _distanceInfo);1128//## temp = adjustFieldTypes(temp, current, false);1129//## int foundMask = startingMask & ~_distanceInfo.missingFieldMask;1130//## int topField = getTopBitNumber(foundMask);1131//## resultPattern = MessageFormat.format(getAppendFormat(topField), new Object[]{resultPattern, temp, getAppendName(topField)});1132//## }1133//## }1134//## return resultPattern;1135//## }1136//## 1137//## private String getAppendName(int foundMask) {1138//## return "'" + appendItemNames[foundMask] + "'";1139//## }1140//## private String getAppendFormat(int foundMask) {1141//## return appendItemFormats[foundMask];1142//## }1143//## 1144//## /**1145//## * @param current21146//## * @return1147//## */1148//## private String adjustSeconds(DateTimeMatcher current2) {1149//## // TODO Auto-generated method stub1150//## return null;1151//## }1152//## 1153//## /**1154//## * @param foundMask1155//## * @return1156//## */1157//## private int getTopBitNumber(int foundMask) {1158//## int i = 0;1159//## while (foundMask != 0) {1160//## foundMask >>>= 1;1161//## ++i;1162//## }1163//## return i-1;1164//## }1165//## 1166//## /**1167//## * 1168//## */1169//## private void complete() {1170//## PatternInfo patternInfo = new PatternInfo();1171//## // make sure that every valid field occurs once, with a "default" length1172//## for (int i = 0; i < CANONICAL_ITEMS.length; ++i) {1173//## char c = (char)types[i][0];1174//## add(String.valueOf(CANONICAL_ITEMS[i]), false, patternInfo);1175//## }1176//## isComplete = true;1177//## }1178//## {1179//## complete();1180//## }1181//## 1182//## /**1183//## * 1184//## */1185//## private String getBestRaw(DateTimeMatcher source, int includeMask, DistanceInfo missingFields) {1186//##// if (SHOW_DISTANCE) System.out.println("Searching for: " + source.pattern 1187//##// + ", mask: " + showMask(includeMask));1188//## int bestDistance = Integer.MAX_VALUE;1189//## String bestPattern = "";1190//## DistanceInfo tempInfo = new DistanceInfo();1191//## for (Iterator it = skeleton2pattern.keySet().iterator(); it.hasNext();) {1192//## DateTimeMatcher trial = (DateTimeMatcher) it.next();1193//## if (trial.equals(skipMatcher)) continue;1194//## int distance = source.getDistance(trial, includeMask, tempInfo);1195//##// if (SHOW_DISTANCE) System.out.println("\tDistance: " + trial.pattern + ":\t" 1196//##// + distance + ",\tmissing fields: " + tempInfo);1197//## if (distance < bestDistance) {1198//## bestDistance = distance;1199//## bestPattern = (String) skeleton2pattern.get(trial);1200//## missingFields.setTo(tempInfo);1201//## if (distance == 0) break;1202//## }1203//## }1204//## return bestPattern;1205//## }1206//## 1207//## /**1208//## * @param fixFractionalSeconds TODO1209//## * 1210//## */1211//## private String adjustFieldTypes(String pattern, DateTimeMatcher inputRequest, boolean fixFractionalSeconds) {1212//## fp.set(pattern);1213//## StringBuffer newPattern = new StringBuffer();1214//## for (Iterator it = fp.getItems().iterator(); it.hasNext();) {1215//## Object item = it.next();1216//## if (item instanceof String) {1217//## newPattern.append(fp.quoteLiteral((String)item));1218//## } else {1219//## String field = ((VariableField) item).string;1220//## int canonicalIndex = getCanonicalIndex(field);1221//## if (canonicalIndex < 0) {1222//## continue; // don't adjust1223//## }1224//## int type = types[canonicalIndex][1];1225//## if (fixFractionalSeconds && type == SECOND) {1226//## String newField = inputRequest.original[FRACTIONAL_SECOND];1227//## field = field + decimal + newField;1228//## } else if (inputRequest.type[type] != 0) {1229//## String newField = inputRequest.original[type];1230//## // normally we just replace the field. However HOUR is special; we only change the length1231//## if (type != HOUR) {1232//## field = newField;1233//## } else if (field.length() != newField.length()){1234//## char c = field.charAt(0);1235//## field = "";1236//## for (int i = newField.length(); i > 0; --i) field += c;1237//## }1238//## }1239//## newPattern.append(field);1240//## }1241//## }1242//## //if (SHOW_DISTANCE) System.out.println("\tRaw: " + pattern);1243//## return newPattern.toString();1244//## }1245//## 1246//##// public static String repeat(String s, int count) {1247//##// StringBuffer result = new StringBuffer();1248//##// for (int i = 0; i < count; ++i) {1249//##// result.append(s);1250//##// }1251//##// return result.toString();1252//##// }1253//## 1254//## /**1255//## * internal routine1256//## * @param pattern1257//## * @return field value1258//## * @deprecated1259//## * @internal1260//## */1261//## public String getFields(String pattern) {1262//## fp.set(pattern);1263//## StringBuffer newPattern = new StringBuffer();1264//## for (Iterator it = fp.getItems().iterator(); it.hasNext();) {1265//## Object item = it.next();1266//## if (item instanceof String) {1267//## newPattern.append(fp.quoteLiteral((String)item));1268//## } else {1269//## newPattern.append("{" + getName(item.toString()) + "}");1270//## }1271//## }1272//## return newPattern.toString();1273//## }1274//## 1275//## private static String showMask(int mask) {1276//## String result = "";1277//## for (int i = 0; i < TYPE_LIMIT; ++i) {1278//## if ((mask & (1<<i)) == 0) continue;1279//## if (result.length() != 0) result += " | ";1280//## result += FIELD_NAME[i] + " ";1281//## }1282//## return result;1283//## }1284//## 1285//## static private String[] CLDR_FIELD_APPEND = {1286//## "Era", "Year", "Quarter", "Month", "Week", "*", "Day-Of-Week", 1287//## "Day", "*", "*", "*", 1288//## "Hour", "Minute", "Second", "*", "Timezone"1289//## };1290//## 1291//## static private String[] CLDR_FIELD_NAME = {1292//## "era", "year", "quarter", "month", "week", "*", "weekday", 1293//## "day", "*", "*", "dayperiod", 1294//## "hour", "minute", "second", "*", "zone"1295//## };1296//## 1297//## static private String[] FIELD_NAME = {1298//## "Era", "Year", "Quarter", "Month", "Week_in_Year", "Week_in_Month", "Weekday", 1299//## "Day", "Day_Of_Year", "Day_of_Week_in_Month", "Dayperiod", 1300//## "Hour", "Minute", "Second", "Fractional_Second", "Zone"1301//## };1302//## 1303//## 1304//## static private String[] CANONICAL_ITEMS = {1305//## "G", "y", "Q", "M", "w", "W", "e", 1306//## "d", "D", "F", 1307//## "H", "m", "s", "S", "v"1308//## };1309//## 1310//## static private Set CANONICAL_SET = new HashSet(Arrays.asList(CANONICAL_ITEMS));1311//## 1312//## static final private int 1313//## DATE_MASK = (1<<DAYPERIOD) - 1,1314//## TIME_MASK = (1<<TYPE_LIMIT) - 1 - DATE_MASK;1315//## 1316//## static final private int // numbers are chosen to express 'distance'1317//## DELTA = 0x10,1318//## NUMERIC = 0x100,1319//## NONE = 0,1320//## NARROW = -0x100,1321//## SHORT = -0x101,1322//## LONG = -0x102,1323//## EXTRA_FIELD = 0x10000,1324//## MISSING_FIELD = 0x1000;1325//## 1326//## 1327//## static private String getName(String s) {1328//## int i = getCanonicalIndex(s);1329//## String name = FIELD_NAME[types[i][1]];1330//## int subtype = types[i][2];1331//## boolean string = subtype < 0;1332//## if (string) subtype = -subtype;1333//## if (subtype < 0) name += ":S";1334//## else name += ":N";1335//## return name;1336//## }1337//## 1338//## static private int getCanonicalIndex(String s) {1339//## int len = s.length();1340//## int ch = s.charAt(0);1341//## for (int i = 0; i < types.length; ++i) {1342//## int[] row = types[i];1343//## if (row[0] != ch) continue;1344//## if (row[3] > len) continue;1345//## if (row[row.length-1] < len) continue;1346//## return i;1347//## }1348//## return -1;1349//## }1350//## 1351//## static private int[][] types = {1352//## // the order here makes a difference only when searching for single field.1353//## // format is:1354//## // pattern character, main type, weight, min length, weight1355//## {'G', ERA, SHORT, 1, 3},1356//## {'G', ERA, LONG, 4},1357//## 1358//## {'y', YEAR, NUMERIC, 1, 20},1359//## {'Y', YEAR, NUMERIC + DELTA, 1, 20},1360//## {'u', YEAR, NUMERIC + 2*DELTA, 1, 20},1361//## 1362//## {'Q', QUARTER, NUMERIC, 1, 2},1363//## {'Q', QUARTER, SHORT, 3},1364//## {'Q', QUARTER, LONG, 4},1365//## 1366//## {'M', MONTH, NUMERIC, 1, 2},1367//## {'M', MONTH, SHORT, 3},1368//## {'M', MONTH, LONG, 4},1369//## {'M', MONTH, NARROW, 5},1370//## {'L', MONTH, NUMERIC + DELTA, 1, 2},1371//## {'L', MONTH, SHORT - DELTA, 3},1372//## {'L', MONTH, LONG - DELTA, 4},1373//## {'L', MONTH, NARROW - DELTA, 5},1374//## 1375//## {'w', WEEK_OF_YEAR, NUMERIC, 1, 2},1376//## {'W', WEEK_OF_MONTH, NUMERIC + DELTA, 1},1377//## 1378//## {'e', WEEKDAY, NUMERIC + DELTA, 1, 2},1379//## {'e', WEEKDAY, SHORT - DELTA, 3},1380//## {'e', WEEKDAY, LONG - DELTA, 4},1381//## {'e', WEEKDAY, NARROW - DELTA, 5},1382//## {'E', WEEKDAY, SHORT, 1, 3},1383//## {'E', WEEKDAY, LONG, 4},1384//## {'E', WEEKDAY, NARROW, 5},1385//## {'c', WEEKDAY, NUMERIC + 2*DELTA, 1, 2},1386//## {'c', WEEKDAY, SHORT - 2*DELTA, 3},1387//## {'c', WEEKDAY, LONG - 2*DELTA, 4},1388//## {'c', WEEKDAY, NARROW - 2*DELTA, 5},1389//## 1390//## {'d', DAY, NUMERIC, 1, 2},1391//## {'D', DAY_OF_YEAR, NUMERIC + DELTA, 1, 3},1392//## {'F', DAY_OF_WEEK_IN_MONTH, NUMERIC + 2*DELTA, 1},1393//## {'g', DAY, NUMERIC + 3*DELTA, 1, 20}, // really internal use, so we don't care1394//## 1395//## {'a', DAYPERIOD, SHORT, 1},1396//## 1397//## {'H', HOUR, NUMERIC + 10*DELTA, 1, 2}, // 24 hour1398//## {'k', HOUR, NUMERIC + 11*DELTA, 1, 2},1399//## {'h', HOUR, NUMERIC, 1, 2}, // 12 hour1400//## {'K', HOUR, NUMERIC + DELTA, 1, 2},1401//## 1402//## {'m', MINUTE, NUMERIC, 1, 2},1403//## 1404//## {'s', SECOND, NUMERIC, 1, 2},1405//## {'S', FRACTIONAL_SECOND, NUMERIC + DELTA, 1, 1000},1406//## {'A', SECOND, NUMERIC + 2*DELTA, 1, 1000},1407//## 1408//## {'v', ZONE, SHORT - 2*DELTA, 1},1409//## {'v', ZONE, LONG - 2*DELTA, 4},1410//## {'z', ZONE, SHORT, 1, 3},1411//## {'z', ZONE, LONG, 4},1412//## {'Z', ZONE, SHORT - DELTA, 1, 3},1413//## {'Z', ZONE, LONG - DELTA, 4},1414//## };1415//## 1416//## private static class DateTimeMatcher implements Comparable {1417//## //private String pattern = null;1418//## private int[] type = new int[TYPE_LIMIT];1419//## private String[] original = new String[TYPE_LIMIT];1420//## private String[] baseOriginal = new String[TYPE_LIMIT];1421//## 1422//## // just for testing; fix to make multi-threaded later1423//## // private static FormatParser fp = new FormatParser();1424//## 1425//## public String toString() {1426//## StringBuffer result = new StringBuffer();1427//## for (int i = 0; i < TYPE_LIMIT; ++i) {1428//## if (original[i].length() != 0) result.append(original[i]);1429//## }1430//## return result.toString();1431//## }1432//## 1433//## String getBasePattern() {1434//## StringBuffer result = new StringBuffer();1435//## for (int i = 0; i < TYPE_LIMIT; ++i) {1436//## if (baseOriginal[i].length() != 0) result.append(baseOriginal[i]);1437//## }1438//## return result.toString();1439//## }1440//## 1441//## DateTimeMatcher set(String pattern, FormatParser fp) {1442//## if (pattern.indexOf("\\u") >= 0) {1443//## String oldPattern = pattern;1444//## pattern = fromHex.transliterate(pattern);1445//## }1446//## for (int i = 0; i < TYPE_LIMIT; ++i) {1447//## type[i] = NONE;1448//## original[i] = "";1449//## baseOriginal[i] = "";1450//## }1451//## fp.set(pattern);1452//## for (Iterator it = fp.getVariableFields(new ArrayList()).iterator(); it.hasNext();) {1453//## String field = (String) it.next();1454//## if (field.charAt(0) == 'a') continue; // skip day period, special cass1455//## int canonicalIndex = getCanonicalIndex(field);1456//## if (canonicalIndex < 0) {1457//## throw new IllegalArgumentException("Illegal field:\t"1458//## + field + "\t in " + pattern);1459//## }1460//## int[] row = types[canonicalIndex];1461//## int typeValue = row[1];1462//## if (original[typeValue].length() != 0) {1463//## throw new IllegalArgumentException("Conflicting fields:\t"1464//## + original[typeValue] + ", " + field + "\t in " + pattern);1465//## }1466//## original[typeValue] = field;1467//## char repeatChar = (char)row[0];1468//## int repeatCount = row[3];1469//## if (repeatCount > 3) repeatCount = 3; // hack to discard differences1470//## if ("GEzvQ".indexOf(repeatChar) >= 0) repeatCount = 1;1471//## baseOriginal[typeValue] = Utility.repeat(String.valueOf(repeatChar),repeatCount);1472//## int subTypeValue = row[2];1473//## if (subTypeValue > 0) subTypeValue += field.length();1474//## type[typeValue] = (byte) subTypeValue;1475//## }1476//## return this;1477//## }1478//## 1479//## /**1480//## * 1481//## */1482//## int getFieldMask() {1483//## int result = 0;1484//## for (int i = 0; i < type.length; ++i) {1485//## if (type[i] != 0) result |= (1<<i);1486//## }1487//## return result;1488//## }1489//## 1490//## /**1491//## * 1492//## */1493//## void extractFrom(DateTimeMatcher source, int fieldMask) {1494//## for (int i = 0; i < type.length; ++i) {1495//## if ((fieldMask & (1<<i)) != 0) {1496//## type[i] = source.type[i];1497//## original[i] = source.original[i];1498//## } else {1499//## type[i] = NONE;1500//## original[i] = "";1501//## }1502//## }1503//## }1504//## 1505//## int getDistance(DateTimeMatcher other, int includeMask, DistanceInfo distanceInfo) {1506//## int result = 0;1507//## distanceInfo.clear();1508//## for (int i = 0; i < type.length; ++i) {1509//## int myType = (includeMask & (1<<i)) == 0 ? 0 : type[i];1510//## int otherType = other.type[i];1511//## if (myType == otherType) continue; // identical (maybe both zero) add 01512//## if (myType == 0) { // and other is not1513//## result += EXTRA_FIELD;1514//## distanceInfo.addExtra(i);1515//## } else if (otherType == 0) { // and mine is not1516//## result += MISSING_FIELD;1517//## distanceInfo.addMissing(i);1518//## } else {1519//## result += Math.abs(myType - otherType); // square of mismatch1520//## }1521//## }1522//## return result;1523//## }1524//## 1525//## public int compareTo(Object o) {1526//## DateTimeMatcher that = (DateTimeMatcher) o;1527//## for (int i = 0; i < original.length; ++i) {1528//## int comp = original[i].compareTo(that.original[i]);1529//## if (comp != 0) return -comp;1530//## }1531//## return 0;1532//## } 1533//## 1534//## public boolean equals(Object other) {1535//## if (other == null) return false;1536//## DateTimeMatcher that = (DateTimeMatcher) other;1537//## for (int i = 0; i < original.length; ++i) {1538//## if (!original[i].equals(that.original[i])) return false;1539//## }1540//## return true;1541//## } 1542//## public int hashCode() {1543//## int result = 0;1544//## for (int i = 0; i < original.length; ++i) {1545//## result ^= original[i].hashCode();1546//## }1547//## return result;1548//## } 1549//## }1550//## 1551//## private static class DistanceInfo {1552//## int missingFieldMask;1553//## int extraFieldMask;1554//## void clear() {1555//## missingFieldMask = extraFieldMask = 0;1556//## }1557//## /**1558//## * 1559//## */1560//## void setTo(DistanceInfo other) {1561//## missingFieldMask = other.missingFieldMask;1562//## extraFieldMask = other.extraFieldMask;1563//## }1564//## void addMissing(int field) {1565//## missingFieldMask |= (1<<field);1566//## }1567//## void addExtra(int field) {1568//## extraFieldMask |= (1<<field);1569//## }1570//## public String toString() {1571//## return "missingFieldMask: " + DateTimePatternGenerator.showMask(missingFieldMask)1572//## + ", extraFieldMask: " + DateTimePatternGenerator.showMask(extraFieldMask);1573//## }1574//## }1575//##}1576//#endif1577//eof1578