Package org.zkoss.zss.model.impl

Source Code of org.zkoss.zss.model.impl.DateInputMask

/* DateInputFormat.java

  Purpose:
   
  Description:
   
  History:
    Oct 1, 2010 10:05:57 AM, Created by henrichen

Copyright (C) 2010 Potix Corporation. All Rights Reserved.
*/

package org.zkoss.zss.model.impl;

import java.text.AttributedCharacterIterator;
import java.text.CharacterIterator;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.zkoss.poi.ss.usermodel.ZssContext;
import org.zkoss.util.CacheMap;

/**
* Responsible for date input mask.
* @author henrichen
*
*/
public class DateInputMask { //ZSS-67
  private static final String SPACES = "[ \\t]*";
  private static final String SPACE1 = "[ \\t]+";
  private static final String START_ANCHOR = "^"+SPACES;
  private static final String END_ANCHOR = SPACES+"$";
  private static final String AMPM = "[AP]M?";
  private static final String AMPM1_G = "("+AMPM+")";
  private static final String COMMA = SPACES+"[,]"+ SPACE1;
  private static final String DASH_SLASH_SPACE = "(?:"+SPACES+"[-/]?|[-/]?)"+SPACES; // dash, slash, or space
  private static final String DOT_DASH_SLASH_SPACE = "(?:"+SPACES+"[-/.]?|[-/.]?)"+SPACES; // dot, dash, slash, or space
  private static final String DOT_SPACE = "(?:"+SPACES+"[.]?|[.]?)"+SPACES; //dot or space
  private static final String COLON_SPACE = "(?:"+SPACES+"[:]?|[:]?)"+SPACES; // colon or space
  private static final String DOT_DASH_SLASH = SPACES+"[-/.]"+SPACES; // dot, dash, or slash
  private static final String DASH_SLASH = SPACES+"[/-]"+SPACES; // dash or slash
  private static final String COLON_AMPM = "(?:"+SPACES+"[:]|"+SPACE1+AMPM1_G+")"+SPACES; //colon or am/pm
  private static final String NAME_MONTH_G = "([A-Z\\p{InLatin-1Supplement}]{3,9})"; //Named Month: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
//  private static final String NAME_MONTH_G = "([\\p{InBasicLatin}\\p{InLatin-1Supplement}]{3,9})"; //Named Month: Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
  private static final String NUM_MONTH_G = "([0]?[1-9]|1[012])"; //Numbed Month: 1 ~ 12
  private static final String DAY_G = "([0]?[1-9]|[12][0-9]|3[01])"; //Day: 1 ~ 31
  private static final String YEAR_G = "(19\\d\\d|[2-9]\\d\\d\\d|\\d\\d|\\d)"; //1900 - 9999
 
  private static final String HOUR_G = "(\\d{1,4})";
  private static final String MINUTE_G = "(\\d{1,4})";
  private static final String SECOND_G = "(\\d{0,4})";
  private static final String MSECOND_G = "(\\d{0,4})";
  private static final String AMPM2_G = "("+SPACE1+AMPM+"|"+AMPM+")?";
  private static final String TIME = HOUR_G + COLON_AMPM
    + "(?:" + MINUTE_G + COLON_SPACE + "|" + SPACES + ")"
    + SECOND_G + DOT_SPACE + MSECOND_G + AMPM2_G;
 
  private static final String TIME_PAT = START_ANCHOR + TIME + END_ANCHOR;
 
//US date format input parse pattern 
/*  private static final String DATE11 = NAME_MONTH_G + DASH_SLASH_SPACE + DAY_G + COMMA + YEAR_G; //Feb 1, 2010 || Feb-1, 2010 => apply "d-mmm-yy"  
  private static final String DATE12 = DAY_G + DASH_SLASH_SPACE + NAME_MONTH_G + DASH_SLASH_SPACE + YEAR_G; //1 Feb 2010 || 1 - Feb - 2010 || 1 / Feb / 2000 => apply "d-mmm-yy" 
  private static final String DATE21 = NAME_MONTH_G + DASH_SLASH_SPACE + DAY_G; //Feb 1 || Feb-1 || Feb/1 => apply "d-mmm" 
  private static final String DATE22 = NUM_MONTH_G + DASH_SLASH + DAY_G; //2-1 || 2/1  => apply "d-mmm"; 2-31 => apply "mmm-yy" if date over month's limit 
  private static final String DATE31 = NAME_MONTH_G + DASH_SLASH_SPACE + YEAR_G; //Feb 31 || Feb-31 || Feb/31 => apply "mmm-yy"
  private static final String DATE32 = NUM_MONTH_G + DASH_SLASH + YEAR_G; //2-31 || 2/31 => apply "mmm-yy"
  private static final String DATE4 = NUM_MONTH_G + DASH_SLASH + DAY_G + DASH_SLASH + YEAR_G; //2-1-2010 || 2/1/2010 || 2/1-2010 || 2-1/2010 => apply "m/d/yyyy"

  private static final String DATE11_PAT = START_ANCHOR + DATE11 + END_ANCHOR;  
  private static final String DATE12_PAT = START_ANCHOR + DATE12 + END_ANCHOR; 
  private static final String DATE21_PAT = START_ANCHOR + DATE21 + END_ANCHOR; 
  private static final String DATE22_PAT = START_ANCHOR + DATE22 + END_ANCHOR; 
  private static final String DATE31_PAT = START_ANCHOR + DATE31 + END_ANCHOR;
  private static final String DATE32_PAT = START_ANCHOR + DATE32 + END_ANCHOR;
  private static final String DATE4_PAT = START_ANCHOR + DATE4 + END_ANCHOR;
 
  private static final String DATETIME11_PAT = START_ANCHOR + DATE11 + SPACE1 + TIME + END_ANCHOR;  
  private static final String DATETIME12_PAT = START_ANCHOR + DATE12 + SPACE1 + TIME + END_ANCHOR; 
  private static final String DATETIME21_PAT = START_ANCHOR + DATE21 + SPACE1 + TIME + END_ANCHOR; 
  private static final String DATETIME22_PAT = START_ANCHOR + DATE22 + SPACE1 + TIME + END_ANCHOR; 
  private static final String DATETIME31_PAT = START_ANCHOR + DATE31 + SPACE1 + TIME + END_ANCHOR;
  private static final String DATETIME32_PAT = START_ANCHOR + DATE32 + SPACE1 + TIME + END_ANCHOR;
  private static final String DATETIME4_PAT = START_ANCHOR + DATE4 + SPACE1 + TIME + END_ANCHOR;
*/
    private static final int flags = Pattern.CASE_INSENSITIVE;
  private static final CacheMap _formatInfosMap; //locale -> FormatInfo[]
  static {
    _formatInfosMap = new CacheMap(4);
    _formatInfosMap.setLifetime(24*60*60*1000);
  }
   
  //"JUL 18, 65" or "18 JUL 65"
  private String[] getDate11(Locale locale, boolean withTime) {
    DateFormat format = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
    final TimeZone gmt = TimeZone.getTimeZone("GMT");
    format.setTimeZone(gmt);
    final Calendar cal = Calendar.getInstance(gmt);
    cal.set(65, 6, 18, 0, 0, 0);
    final AttributedCharacterIterator iter
      = format.formatToCharacterIterator(cal.getTime());
   
    final StringBuilder sb = new StringBuilder(); //generated regular expression
    final StringBuilder sb1 = new StringBuilder(); //sample input text
    boolean alreadyDate = false;
    boolean alreadyMonth = false;
    boolean alreadyYear = false;
    int leadingSpace = -2;
    int leadingComma = -2;
    int j = 0;
    sb.append(START_ANCHOR);
    for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
      final Map attrs = iter.getAttributes();
      if (attrs.isEmpty()) {
        switch(c) {
        case ','//comma
          leadingComma = j;
          break;
        case ' ': //space
          if (leadingComma == (j-1)) {
            sb.append(COMMA);
          } else if (leadingSpace != (j-1)) {
            sb.append(DASH_SLASH_SPACE);
          }
          leadingSpace = j;
          break;
        case '.': //dot
        case '-': //dash //ZSS-122: in Japan medium DateFormat is 1965/07/18
        case '/': //slash
          if (leadingSpace != (j-1)) {
            sb.append(DOT_DASH_SLASH_SPACE);
          }
          leadingSpace = j;
        }
        sb1.append(c);
      } else {
        for(DateFormat.Field field : (Set<DateFormat.Field>) attrs.keySet()) {
          switch(field.getCalendarField()) {
          case Calendar.DAY_OF_MONTH:
            if (!alreadyDate) {
              alreadyDate = true;
              sb.append(DAY_G);
              sb1.append("18");
            }
            break;
          case Calendar.MONTH:
            if (!alreadyMonth) {
              alreadyMonth = true;
              sb.append(NAME_MONTH_G);
              sb1.append("JUL");
            }
            break;
          case Calendar.YEAR:
            if (!alreadyYear) {
              alreadyYear = true;
              sb.append(YEAR_G);
              sb1.append("65");
            }
            break;
          }
        }
      }
      ++j;
    }
    final StringBuilder sb2 = new StringBuilder(); //reqular expression with time part if any
    if (withTime) {
      sb.append(SPACE1).append(TIME);
      sb2.append(sb1).append(" 2 P");
      sb1.append(" 2:3:5.100 A");
    }
    sb.append(END_ANCHOR);
    return withTime ?
        new String[] {sb.toString(), sb1.toString(), sb2.toString()} :
        new String[] {sb.toString(), sb1.toString()};
  }

  private void enforceDMY(StringBuilder sb, StringBuilder sb1, int attrCount) {
    switch(attrCount) {
    case 0: //date
      sb.append(DAY_G);
      sb1.append("18");
      break;
    case 1: //month
      sb.append(NAME_MONTH_G);
      sb1.append("JUL");
      break;
    case 2: //year
      sb.append(YEAR_G);
      sb1.append("65");
      break;
    }
  }
 
  //"18 JUL 65"
  private String[] getDate12(Locale locale, boolean withTime) {
    DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale);
    final TimeZone gmt = TimeZone.getTimeZone("GMT");
    format.setTimeZone(gmt);
    final Calendar cal = Calendar.getInstance(gmt);
    cal.set(65, 6, 18, 0, 0, 0);
    final AttributedCharacterIterator iter
      = format.formatToCharacterIterator(cal.getTime());
   
    final StringBuilder sb = new StringBuilder();
    final StringBuilder sb1 = new StringBuilder();
    boolean alreadyDate = false;
    boolean alreadyMonth = false;
    boolean alreadyYear = false;
    int leadingSpace = -2;
    int attrCount = 0;
    int j = 0;
    sb.append(START_ANCHOR);
    for(char c = iter.first(); c != CharacterIterator.DONE && attrCount < 3; c = iter.next()) {
      final Map attrs = iter.getAttributes();
      if (attrs.isEmpty()) {
        if (leadingSpace != (j-1)) {
          if (c == '.') {
            sb.append(DOT_DASH_SLASH_SPACE);
          } else {
            sb.append(DASH_SLASH_SPACE);
          }
          sb1.append(' ');
        }
        leadingSpace = j;
      } else {
        for(DateFormat.Field field : (Set<DateFormat.Field>) attrs.keySet()) {
          switch(field.getCalendarField()) {
          case Calendar.DAY_OF_MONTH:
            if (!alreadyDate) {
              alreadyDate = true;
              enforceDMY(sb, sb1, attrCount);
              ++attrCount;
            }
            break;
          case Calendar.MONTH:
            if (!alreadyMonth) {
              alreadyMonth = true;
              enforceDMY(sb, sb1, attrCount);
              ++attrCount;
            }
            break;
          case Calendar.YEAR:
            if (!alreadyYear) {
              alreadyYear = true;
              enforceDMY(sb, sb1, attrCount);
              ++attrCount;
            }
            break;
          }
        }
      }
      ++j;
    }
    final StringBuilder sb2 = new StringBuilder();
    if (withTime) {
      sb.append(SPACE1).append(TIME);
      sb2.append(sb1).append(" 2 P");
      sb1.append(" 2:3:5.100 A");
    }
    sb.append(END_ANCHOR);
    return withTime ?
        new String[] {sb.toString(), sb1.toString(), sb2.toString()} :
        new String[] {sb.toString(), sb1.toString()};
  }
 
  //"JUL 18" or "18 JUL":  (M-d), (M-y), (d-M), (y-M)
  private String[] getDate21(Locale locale, boolean withTime) {
    DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale);
    final TimeZone gmt = TimeZone.getTimeZone("GMT");
    format.setTimeZone(gmt);
    final Calendar cal = Calendar.getInstance(gmt);
    cal.set(65, 6, 18, 0, 0, 0);
    final AttributedCharacterIterator iter
      = format.formatToCharacterIterator(cal.getTime());
   
    final StringBuilder sb = new StringBuilder();
    final StringBuilder sb1 = new StringBuilder();
    boolean alreadyDate = false;
    boolean alreadyMonth = false;
    boolean alreadyYear = false;
    int attrCount = 0;
    int leadingSpace = -2;
    int j = 0;
    sb.append(START_ANCHOR);
    for(char c = iter.first(); c != CharacterIterator.DONE && attrCount < 2; c = iter.next()) {
      final Map attrs = iter.getAttributes();
      if (attrs.isEmpty()) {
        if (leadingSpace != (j-1)) {
          if (c == '.') {
            sb.append(DOT_DASH_SLASH_SPACE);
          } else {
            sb.append(DASH_SLASH_SPACE);
          }
          sb1.append(' ');
        }
        leadingSpace = j;
      } else {
        for(DateFormat.Field field : (Set<DateFormat.Field>) attrs.keySet()) {
          switch(field.getCalendarField()) {
          case Calendar.DAY_OF_MONTH:
            if (!alreadyDate) {
              alreadyDate = true;
              sb.append(DAY_G);
              sb1.append("18");
              ++attrCount;
            }
            break;
          case Calendar.MONTH:
            if (!alreadyMonth) {
              alreadyMonth = true;
              sb.append(NAME_MONTH_G);
              sb1.append("JUL");
              ++attrCount;
            }
            break;
          case Calendar.YEAR:
            if (!alreadyYear) {
              alreadyYear = true;
              sb.append(YEAR_G);
              sb1.append("65");
              ++attrCount;
            }
            break;
          }
        }
      }
      ++j;
    }
    final StringBuilder sb2 = new StringBuilder();
    if (withTime) {
      sb.append(SPACE1).append(TIME);
      sb2.append(sb1).append(" 2 P");
      sb1.append(" 2:3:5.100 A");
    }
    sb.append(END_ANCHOR);
    return withTime ?
        new String[] {sb.toString(), sb1.toString(), sb2.toString()} :
        new String[] {sb.toString(), sb1.toString()};
  }
 
  //"7-18": (M-d), (M-y), (d-M), (y-M)
  private String[] getDate22(Locale locale, boolean withTime) {
    DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale);
    final TimeZone gmt = TimeZone.getTimeZone("GMT");
    format.setTimeZone(gmt);
    final Calendar cal = Calendar.getInstance(gmt);
    cal.set(65, 6, 18, 0, 0, 0);
    final AttributedCharacterIterator iter
      = format.formatToCharacterIterator(cal.getTime());
   
    final StringBuilder sb = new StringBuilder();
    final StringBuilder sb1 = new StringBuilder();
    boolean alreadyDate = false;
    boolean alreadyMonth = false;
    boolean alreadyYear = false;
    int leadingSpace = -2;
    int j = 0;
    int attrCount = 0;
    sb.append(START_ANCHOR);
    for(char c = iter.first(); c != CharacterIterator.DONE && attrCount < 2; c = iter.next()) {
      final Map attrs = iter.getAttributes();
      if (attrs.isEmpty()) {
        if (leadingSpace != (j-1)) {
          if ('.' == c) {
            sb.append(DOT_DASH_SLASH);
          } else {
            sb.append(DASH_SLASH);
          }
          sb1.append('-');
        }
        leadingSpace = j;
      } else {
        for(DateFormat.Field field : (Set<DateFormat.Field>) attrs.keySet()) {
          switch(field.getCalendarField()) {
          case Calendar.DAY_OF_MONTH:
            if (!alreadyDate) {
              alreadyDate = true;
              sb.append(DAY_G);
              sb1.append("18");
              ++attrCount;
            }
            break;
          case Calendar.MONTH:
            if (!alreadyMonth) {
              alreadyMonth = true;
              sb.append(NUM_MONTH_G);
              sb1.append("7");
              ++attrCount;
            }
            break;
          case Calendar.YEAR:
            if (!alreadyYear) {
              alreadyYear = true;
              sb.append(YEAR_G);
              sb1.append("65");
              ++attrCount;
            }
            break;
          }
        }
      }
      ++j;
    }
    final StringBuilder sb2 = new StringBuilder();
    if (withTime) {
      sb.append(SPACE1).append(TIME);
      sb2.append(sb1).append(" 2 P");
      sb1.append(" 2:3:5.100 A");
    }
    sb.append(END_ANCHOR);
    return withTime ?
        new String[] {sb.toString(), sb1.toString(), sb2.toString()} :
        new String[] {sb.toString(), sb1.toString()};
  }
 
  //"JUL 65": supplement format for date21. That is, if date21 return (M-d), then have to check (M-y)
  private String[] getDate31(Locale locale, boolean withTime) {
    DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale);
    final TimeZone gmt = TimeZone.getTimeZone("GMT");
    format.setTimeZone(gmt);
    final Calendar cal = Calendar.getInstance(gmt);
    cal.set(65, 6, 18, 0, 0, 0);
    final AttributedCharacterIterator iter
      = format.formatToCharacterIterator(cal.getTime());
   
    final StringBuilder sb = new StringBuilder();
    final StringBuilder sb1 = new StringBuilder();
    boolean alreadyDate = false;
    boolean alreadyMonth = false;
    boolean alreadyYear = false;
    int attrCount = 0;
    int leadingSpace = -2;
    int j = 0;
    sb.append(START_ANCHOR);
    for(char c = iter.first(); c != CharacterIterator.DONE && attrCount < 2; c = iter.next()) {
      final Map attrs = iter.getAttributes();
      if (attrs.isEmpty()) {
        if (leadingSpace != (j-1) && alreadyMonth) {
          if (c == '.') {
            sb.append(DOT_DASH_SLASH_SPACE);
          } else {
            sb.append(DASH_SLASH_SPACE);
          }
          sb1.append(' ');
        }
        leadingSpace = j;
      } else {
        for(DateFormat.Field field : (Set<DateFormat.Field>) attrs.keySet()) {
          switch(field.getCalendarField()) {
          case Calendar.DAY_OF_MONTH:
            if (!alreadyDate && alreadyMonth) {
              alreadyDate = true;
              sb.append(YEAR_G); //enforce year after month format
              sb1.append("65");
              ++attrCount;
            }
            break;
          case Calendar.MONTH:
            if (!alreadyMonth) {
              alreadyMonth = true;
              sb.append(NAME_MONTH_G);
              sb1.append("JUL");
              ++attrCount;
            }
            break;
          case Calendar.YEAR:
            if (!alreadyYear && alreadyMonth) {
              alreadyYear = true;
              sb.append(YEAR_G);
              sb1.append("65");
              ++attrCount;
            }
            break;
          }
        }
      }
      ++j;
    }
    final StringBuilder sb2 = new StringBuilder();
    if (withTime) {
      sb.append(SPACE1).append(TIME);
      sb2.append(sb1).append(" 2 P");
      sb1.append(" 2:3:5.100 A");
    }
    sb.append(END_ANCHOR);
    return withTime ?
        new String[] {sb.toString(), sb1.toString(), sb2.toString()} :
        new String[] {sb.toString(), sb1.toString()};
  }
 
  //"7-65": supplement format for date22. That is, if date22 return (M-d), (d-M), or (y-M), then have to check (M-y)
  private String[] getDate32(Locale locale, boolean withTime) {
    DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale);
    final TimeZone gmt = TimeZone.getTimeZone("GMT");
    format.setTimeZone(gmt);
    final Calendar cal = Calendar.getInstance(gmt);
    cal.set(65, 6, 18, 0, 0, 0);
    final AttributedCharacterIterator iter
      = format.formatToCharacterIterator(cal.getTime());
   
    final StringBuilder sb = new StringBuilder();
    final StringBuilder sb1 = new StringBuilder();
    boolean alreadyDate = false;
    boolean alreadyMonth = false;
    boolean alreadyYear = false;
    int leadingSpace = -2;
    int j = 0;
    int attrCount = 0;
    sb.append(START_ANCHOR);
    for(char c = iter.first(); c != CharacterIterator.DONE && attrCount < 2; c = iter.next()) {
      final Map attrs = iter.getAttributes();
      if (attrs.isEmpty()) {
        if (leadingSpace != (j-1) && attrCount < 2 && alreadyMonth) {
          if ('.' == c) {
            sb.append(DOT_DASH_SLASH);
          } else {
            sb.append(DASH_SLASH);
          }
          sb1.append('-');
        }
        leadingSpace = j;
      } else {
        for(DateFormat.Field field : (Set<DateFormat.Field>) attrs.keySet()) {
          switch(field.getCalendarField()) {
          case Calendar.DAY_OF_MONTH:
            if (!alreadyDate && alreadyMonth) {
              alreadyDate = true;
              sb.append(YEAR_G); //enforce year after month format
              sb1.append("65");
              ++attrCount;
            }
            break;
          case Calendar.MONTH:
            if (!alreadyMonth) {
              alreadyMonth = true;
              sb.append(NUM_MONTH_G);
              sb1.append("7");
              ++attrCount;
            }
            break;
          case Calendar.YEAR:
            if (!alreadyYear && alreadyMonth) {
              alreadyYear = true;
              sb.append(YEAR_G);
              sb1.append("65");
              ++attrCount;
            }
            break;
          }
        }
      }
      ++j;
    }
    final StringBuilder sb2 = new StringBuilder();
    if (withTime) {
      sb.append(SPACE1).append(TIME);
      sb2.append(sb1).append(" 2 P");
      sb1.append(" 2:3:5.100 A");
    }
    sb.append(END_ANCHOR);
    return withTime ?
        new String[] {sb.toString(), sb1.toString(), sb2.toString()} :
        new String[] {sb.toString(), sb1.toString()};
  }
 
  //"7-18-65": (M-d-y), (d-M-y), or (y-M-d)
  private String[] getDate4(Locale locale, boolean withTime) {
    DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT, locale);
    final TimeZone gmt = TimeZone.getTimeZone("GMT");
    format.setTimeZone(gmt);
    final Calendar cal = Calendar.getInstance(gmt);
    cal.set(65, 6, 18, 0, 0, 0);
    final AttributedCharacterIterator iter
      = format.formatToCharacterIterator(cal.getTime());
   
    final StringBuilder sb = new StringBuilder();
    final StringBuilder sb1 = new StringBuilder();
    boolean alreadyDate = false;
    boolean alreadyMonth = false;
    boolean alreadyYear = false;
    int leadingSpace = -2;
    int j = 0;
    sb.append(START_ANCHOR);
    for(char c = iter.first(); c != CharacterIterator.DONE; c = iter.next()) {
      final Map attrs = iter.getAttributes();
      if (attrs.isEmpty()) {
        if (leadingSpace != (j-1)) {
          if (c == '.') {
            sb.append(DOT_DASH_SLASH);
          } else {
            sb.append(DASH_SLASH);
          }
          sb1.append('-');
        }
        leadingSpace = j;
      } else {
        for(DateFormat.Field field : (Set<DateFormat.Field>) attrs.keySet()) {
          switch(field.getCalendarField()) {
          case Calendar.DAY_OF_MONTH:
            if (!alreadyDate) {
              alreadyDate = true;
              sb.append(DAY_G);
              sb1.append("18");
            }
            break;
          case Calendar.MONTH:
            if (!alreadyMonth) {
              alreadyMonth = true;
              sb.append(NUM_MONTH_G);
              sb1.append("7");
            }
            break;
          case Calendar.YEAR:
            if (!alreadyYear) {
              alreadyYear = true;
              sb.append(YEAR_G);
              sb1.append("65");
            }
            break;
          }
        }
      }
      ++j;
    }
    final StringBuilder sb2 = new StringBuilder();
    if (withTime) {
      sb.append(SPACE1).append(TIME);
      sb2.append(sb1).append(" 2 P");
      sb1.append(" 2:3:5.100 A");
    }
    sb.append(END_ANCHOR);
    return withTime ?
        new String[] {sb.toString(), sb1.toString(), sb2.toString()} :
        new String[] {sb.toString(), sb1.toString()};
  }
 
  private FormatInfo[] getFormatInfos(Locale locale) {
    FormatInfo[] formatInfos = (FormatInfo[]) _formatInfosMap.get(locale);
    if (formatInfos != null) { //in cache
      return formatInfos;
    }
    formatInfos= new FormatInfo[15];
        int j = 0;
        //formatInfos[j++] = new FormatInfo(Pattern.compile(DATE11_PAT, flags), "d-mmm-yy", "JUL 18, 65", null, locale);
        final String[] date11 = getDate11(locale, false);
        formatInfos[j++] = new FormatInfo(Pattern.compile(date11[0], flags), "d-mmm-yy", date11[1], null, locale); //0
       
        //formatInfos[j++] = new FormatInfo(Pattern.compile(DATE12_PAT, flags), "d-mmm-yy", "18 JUL 65", null, locale);
        final String[] date12 = getDate12(locale, false);
        formatInfos[j++] = new FormatInfo(Pattern.compile(date12[0], flags), "d-mmm-yy", date12[1], null, locale); //1
       
        //formatInfos[j++] = new FormatInfo(Pattern.compile(DATE21_PAT, flags), "d-mmm", "JUL 18", null, locale);
        final String[] date21 = getDate21(locale, false);
        formatInfos[j++] = new FormatInfo(Pattern.compile(date21[0], flags), "d-mmm", date21[1], null, locale); //2
       
        //formatInfos[j++] = new FormatInfo(Pattern.compile(DATE22_PAT, flags), "d-mmm", "7-18", null, locale);
        final String[] date22 = getDate22(locale, false);
        formatInfos[j++] = new FormatInfo(Pattern.compile(date22[0], flags), "d-mmm", date22[1], null, locale); //3
       
        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATE31_PAT, flags), "mmm-yy", "JUL 65", null, locale);
        final String[] date31 = getDate31(locale, false);
        formatInfos[j++] = new FormatInfo(Pattern.compile(date31[0], flags), "mmm-yy", date31[1], null, locale); //4
       
        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATE32_PAT, flags), "mmm-yy", "7-65", null, locale);
        final String[] date32 = getDate32(locale, false);
        formatInfos[j++] = new FormatInfo(Pattern.compile(date32[0], flags), "mmm-yy", date32[1], null, locale); //5
       
        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATE4_PAT, flags), "m/d/yyyy", "7-18-65", null, locale);
        final String[] date4 = getDate4(locale, false);
        formatInfos[j++] = new FormatInfo(Pattern.compile(date4[0], flags), "m/d/yyyy", date4[1], null, locale); //6

        formatInfos[j++] = new FormatInfo(Pattern.compile(TIME_PAT, flags), null, "2:3:5.100 A", "2 P", locale); //7

        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATETIME11_PAT, flags), "m/d/yyyy h:mm", "JUL 18, 65 2:3:5.100 A", "JUL 18, 65 2 P", locale);
        final String[] datetime11 = getDate11(locale, true);
        formatInfos[j++] = new FormatInfo(Pattern.compile(datetime11[0], flags), "m/d/yyyy h:mm", datetime11[1], datetime11[2], locale); //8

        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATETIME12_PAT, flags), "m/d/yyyy h:mm", "18 JUL 65 2:3:5.100 A", "18 JUL 65 2 P", locale);
        final String[] datetime12 = getDate12(locale, true);
        formatInfos[j++] = new FormatInfo(Pattern.compile(datetime12[0], flags), "m/d/yyyy h:mm", datetime12[1], datetime12[2], locale)//9

        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATETIME21_PAT, flags), "m/d/yyyy h:mm", "JUL 18 2:3:5.100 A", "JUL 18 2 P", locale);
        final String[] datetime21 = getDate21(locale, true);
        formatInfos[j++] = new FormatInfo(Pattern.compile(datetime21[0], flags), "m/d/yyyy h:mm", datetime21[1], datetime21[2], locale); //10

        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATETIME22_PAT, flags), "m/d/yyyy h:mm", "7-18 2:3:5.100 A", "7-18 2 P", locale);
        final String[] datetime22 = getDate22(locale, true);
        formatInfos[j++] = new FormatInfo(Pattern.compile(datetime22[0], flags), "m/d/yyyy h:mm", datetime22[1], datetime22[2], locale); //11

        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATETIME31_PAT, flags), "m/d/yyyy h:mm", "JUL 65 2:3:5.100 A", "JUL 65 2 P", locale);
        final String[] datetime31 = getDate31(locale, true);
        formatInfos[j++] = new FormatInfo(Pattern.compile(datetime31[0], flags), "m/d/yyyy h:mm", datetime31[1], datetime31[2], locale); //12

        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATETIME32_PAT, flags), "m/d/yyyy h:mm", "7-65 2:3:5.100 A", "7-65 2 P", locale);
        final String[] datetime32 = getDate32(locale, true);
        formatInfos[j++] = new FormatInfo(Pattern.compile(datetime32[0], flags), "m/d/yyyy h:mm", datetime32[1], datetime32[2], locale); //13
       
        //_formatInfo[j++] = new FormatInfo(Pattern.compile(DATETIME4_PAT, flags), "m/d/yyyy h:mm", "7-18-65 2:3:5.100 A", "7-18-65 2 P", locale);
        final String[] datetime4 = getDate4(locale, true);
        formatInfos[j++] = new FormatInfo(Pattern.compile(datetime4[0], flags), "m/d/yyyy h:mm", datetime4[1], datetime4[2], locale); //14
       
        //cache it
        _formatInfosMap.put(locale, formatInfos);
    return formatInfos;
  }
  /* @param txt the input text
   */
  public Object[] parseDateInput(String txt) {
    final ZssContext ctx = ZssContext.getCurrent();
    final int twoDigitYearUpperBound = ctx.getTwoDigitYearUpperBound();
    final Locale locale = ctx.getLocale();
    final FormatInfo[] formatInfos = getFormatInfos(locale);
    for(int j = 0; j < formatInfos.length; ++j) {
      final FormatInfo info = formatInfos[j];
          Matcher m = info.getMaskPattern().matcher(txt);
          if (m.matches()) {
            return info.parseInput(txt, m, twoDigitYearUpperBound);
          }
    }
    return new Object[] {txt, null};
  }
  private static class FormatInfo {
    private final Pattern _mask;
    private final String _format;
    private int _year;
    private int _month;
    private int _day;
    private int _hour;
    private int _minute;
    private int _second;
    private int _msecond;
    private int _ampm1;
    private int _ampm2;
    private Locale _locale;
   
    //match text: "Jul 18, 1965 2:3:5.100 A"
    public FormatInfo(Pattern mask, String format, String groupMatchText, String ampm1MatchText, Locale locale) {
/*      System.out.println("---local:"+locale+"---------------------------------");     
      System.out.println("pattern  :"+mask);     
      System.out.println("format   :"+format);     
      System.out.println("groupText:"+groupMatchText);     
      System.out.println("ampm1MatchText:"+ampm1MatchText);     
*/
      _locale = locale;
      _mask = mask;
      _format = format;
          Matcher m = _mask.matcher(groupMatchText);
          if (!m.matches()) {
            throw new RuntimeException("Wrong groupMatchText: "+ groupMatchText+ ", regex mask: "+_mask);
          }
          for (int j = 1, len = m.groupCount()+1; j < len; ++j) {
            final String grptxt = m.group(j);
            if ("65".equals(grptxt)) {
              _year = j;
            } else if ("7".equals(grptxt)) {
              _month = j;
            } else if ("JUL".equals(grptxt)) {
              _month = j;
            } else if ("18".equals(grptxt)) {
              _day = j;
            } else if (" A".equals(grptxt)) {
              _ampm2 = j;
            } else if ("2".equals(grptxt)) {
              _hour = j;
            } else if ("3".equals(grptxt)) {
              _minute = j;
            } else if ("5".equals(grptxt)) {
              _second = j;
            } else if ("100".equals(grptxt)) {
              _msecond = j;
            }
          }
          if (ampm1MatchText != null) {
            m = _mask.matcher(ampm1MatchText);
            if (!m.matches()) {
              throw new RuntimeException("Wrong ampm1MatchText: "+ ampm1MatchText+ ", regex mask: "+_mask);
            }
            for (int j = 1, len = m.groupCount(); j < len; ++j) {
              final String grptxt = m.group(j);
              if ("P".equals(grptxt)) {
                _ampm1 = j;
                break;
              }
            }
          }
    }
   
    public Pattern getMaskPattern() {
      return _mask;
    }
   
    public String getFormat() {
      return _format;
    }
   
    private int getMonthIndex(String month) {
      month = month.toUpperCase();
      FullMonthData fmd = FullMonthData.getInstance(CircularData.UPPER, _locale);
      return fmd.getIndexByStartsWith(month);
    }
   
    //return month index (January is 0)
    private int parseMonth(Matcher m) {
      if (_month > 0) {
        final String month = m.group(_month);
        if (month.length() >= 3) { //Feb case
          return getMonthIndex(month);
        } else {
          return Integer.parseInt(month) - 1;
        }
      }
      return -1;
    }
   
    //return day index (1st day is 1)
    private int parseDay(Matcher m) {
      if (_day > 0) {
        final String day = m.group(_day);
        return Integer.parseInt(day);
      } else if (_day == 0) { //no day part
        return 1; //default to first day
      }
      return -1;
    }
   
    //return year
    private int parseYear(Matcher m, int twoDigitYearUpperBound) {
      if (_year > 0) {
        final String year = m.group(_year);
        int y = Integer.parseInt(year);
        return y < 100 ? twoDigitYearTo4DigitYear(y, twoDigitYearUpperBound) : y;
      }
      return -1;
    }
   
    private int twoDigitYearTo4DigitYear(int y, int twoDigitYearUpperBound) {
      if (twoDigitYearUpperBound < 1999) {
        twoDigitYearUpperBound = (getCalendar().get(Calendar.YEAR) / 10) * 10 + 20 - 1;
      }
      final int upperLowDigit = twoDigitYearUpperBound % 100;
      if (y <= upperLowDigit) {
        return twoDigitYearUpperBound - upperLowDigit + y;
      } else {
        return twoDigitYearUpperBound - upperLowDigit - 100 + y;
      }
    }
   
    private static final int[] MAXDAYS = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    //given month and year, return the maxday
    private int getMaxday(int month, int year) {
      int maxday = MAXDAYS[month];
      if (month == 1 && isLeapYear(year)) {//Feb index = 1
        return maxday + 1;
      }
      return maxday;
    }
    private boolean isLeapYear(int year) {
      return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
    }
    //return hour number
    private int parseHour(Matcher m) {
      return parseTime(m, _hour);
    }
    //return minute number
    private int parseMinute(Matcher m) {
      return parseTime(m, _minute);
    }
    //return second number
    private int parseSecond(Matcher m) {
      return parseTime(m, _second);
    }
    //return millsecnd number
    private int parseMillisecond(Matcher m) {
      return parseTime(m, _msecond);
    }
    //return minute number
    private int parseTime(Matcher m, int groupIndex) {
      if (groupIndex > 0) {
        final String value = m.group(groupIndex);
        if (value != null && value.length() > 0) {
          return Integer.parseInt(value);
        }
      }
      return -1;
    }
    //return ampm
    private String parseAmpm(Matcher m) {
      String result = null;
      if (_ampm2 > 0) {
        result = m.group(_ampm2);
      }
      if (result == null && _ampm1 > 0) {
        result = m.group(_ampm1);
      }
      return result;
    }
   
    //TODO locale and timezone for the calendar?
    private Calendar getCalendar() {
      return Calendar.getInstance();
    }
    public Object[] parseInput(String txt, Matcher m, int twoDigitYearUpperBound) {
      int month = parseMonth(m);
      if (month < 0 && _month > 0) {
        return new Object[] {txt, null};
      }
      int day = parseDay(m);
      if (day < 0 && _day > 0) {
        return new Object[] {txt, null};
      }
      final Calendar cal = getCalendar();
      int year = parseYear(m, twoDigitYearUpperBound);
      if (year < 0 && _format != null) {
        year = cal.get(Calendar.YEAR);
      }
      String format = _format;
      if (_month > 0) {
        int maxday = getMaxday(month, year);
        if (maxday < day) { // illegal date
          if (_year > 0) { 
            return new Object[] {txt, null};
          } else { //month-year case
            if (_hour <= 0) { //if no hour
              format = "mmm-yy";
            }
            year = day + 1900;
            day = 1;
          }
        }
      }
      int hour = parseHour(m);
      int minute = parseMinute(m);
      int second = parseSecond(m);
      int msecond = parseMillisecond(m);
     
      String ampm = parseAmpm(m);
     
      if (ampm != null) {
        //invalid hour, minute, second with am/pm
        if (hour > 12 || minute >= 60 || second >= 60) {
          return new Object[] {txt, null};
        }
        ampm = ampm.toUpperCase();
        if (hour < 12) {
          if (ampm.startsWith("P")) {
            hour += 12;
          }
        } else { //hour == 12
          if (ampm.startsWith("A")) {
            hour = 0;
          }
        }
      }
     
      if ((hour > 23 && minute >= 60)
        || (minute >= 60 && second >=60)
        || (hour > 23 && second >= 60)) {
        return new Object[] {txt, null};
      }
     
      if (format == null) { //pure time pattern
        year = 1900;
        month = 1 - 1; //0 based index
        day = 1;
        if (msecond >= 0) {
          format = "mm:ss.0";
        } else if (ampm != null) {
          format = second >= 0 ? "h:mm:ss AM/PM" : "h:mm AM/PM";
        } else if (hour < 24) {
          if ( minute < 60 && second < 60) {
            format = second > 0 ? "h:mm:ss" : "h:mm";
          } // else a number; so format == null
        } else {
          if (minute < 60 && second < 60) {
            format = "[h]:mm:ss";
          } // else a number; so format == null
        }
      } else {
        if (msecond >= 0) {
          format = "mm:ss.0";
        } else if (hour > 23 || minute >= 60 || second >=60) {
          format = null; //a number
        }
      }
     
      //date
      cal.set(Calendar.YEAR, year);
      cal.set(Calendar.MONTH, month);
      cal.set(Calendar.DAY_OF_MONTH, day);
     
      //time
      if (hour < 0) {
        hour = 0;
      }
      if (minute < 0) {
        minute = 0;
      }
      if (second < 0) {
        second = 0;
      }
      if (msecond < 0) {
        msecond = 0;
      }
      cal.set(Calendar.HOUR_OF_DAY, hour);
      cal.set(Calendar.MINUTE, minute);
      cal.set(Calendar.SECOND, second);
     
      cal.set(Calendar.MILLISECOND, msecond);
      return new Object[] {cal.getTime(), format};
    }
  }
}
TOP

Related Classes of org.zkoss.zss.model.impl.DateInputMask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.