001    package railo.commons.i18n;
002    
003    import java.text.DateFormat;
004    import java.text.SimpleDateFormat;
005    import java.util.ArrayList;
006    import java.util.List;
007    import java.util.Locale;
008    import java.util.Map;
009    import java.util.TimeZone;
010    
011    import org.apache.commons.collections.map.ReferenceMap;
012    
013    import railo.commons.date.TimeZoneConstants;
014    import railo.commons.io.IOUtil;
015    import railo.commons.io.res.Resource;
016    import railo.commons.lang.StringUtil;
017    import railo.runtime.config.Config;
018    import railo.runtime.engine.ThreadLocalPageContext;
019    
020    public class FormatUtil {
021    
022            public static final short FORMAT_TYPE_DATE=1;
023            public static final short FORMAT_TYPE_TIME=2;
024            public static final short FORMAT_TYPE_DATE_TIME=3;
025            public static final short FORMAT_TYPE_DATE_ALL=4;
026     
027            private final static Map<String,DateFormat[]> formats=new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT);
028            
029            public static DateFormat[] getDateTimeFormats(Locale locale,TimeZone tz,boolean lenient) {
030    
031                    String id="dt-"+locale.hashCode()+"-"+tz.getID()+"-"+lenient;
032                    DateFormat[] df=formats.get(id);
033                    if(df==null) {
034                            List<DateFormat> list=new ArrayList<DateFormat>();
035                            list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.FULL,locale));
036                            list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.LONG,locale));
037                            list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.MEDIUM,locale));
038                            list.add(DateFormat.getDateTimeInstance(DateFormat.FULL,DateFormat.SHORT,locale));
039    
040                            list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.FULL,locale));
041                            list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG,locale));
042                            list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.MEDIUM,locale));
043                            list.add(DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.SHORT,locale));
044    
045                            list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.FULL,locale));
046                            list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.LONG,locale));
047                            list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.MEDIUM,locale));
048                            list.add(DateFormat.getDateTimeInstance(DateFormat.MEDIUM,DateFormat.SHORT,locale));
049    
050                            list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.FULL,locale));
051                            list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.LONG,locale));
052                            list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM,locale));
053                            list.add(DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT,locale));
054                            add24(list, locale);
055                            addCustom(list, locale, FORMAT_TYPE_DATE_TIME);
056                            df=list.toArray(new DateFormat[list.size()]);
057                            
058                            for(int i=0;i<df.length;i++){
059                                    df[i].setLenient(lenient);
060                                    df[i].setTimeZone(tz);
061                            }
062                            
063                            formats.put(id, df);
064                    }
065                    
066                    return df;
067            }
068    
069            
070            public static DateFormat[] getDateFormats(Locale locale,TimeZone tz,boolean lenient) {
071                    String id="d-"+locale.hashCode()+"-"+tz.getID()+"-"+lenient;
072                    DateFormat[] df= formats.get(id);
073                    if(df==null) {
074                            List<DateFormat> list=new ArrayList<DateFormat>();
075                            list.add(DateFormat.getDateInstance(DateFormat.FULL,locale));
076                            list.add(DateFormat.getDateInstance(DateFormat.LONG,locale));
077                            list.add(DateFormat.getDateInstance(DateFormat.MEDIUM,locale));
078                            list.add(DateFormat.getDateInstance(DateFormat.SHORT,locale));
079                            addCustom(list, locale, FORMAT_TYPE_DATE);
080                            df=list.toArray(new DateFormat[list.size()]);
081                            
082                            for(int i=0;i<df.length;i++){
083                                    df[i].setLenient(lenient);
084                                    df[i].setTimeZone(tz);
085                            }
086                            formats.put(id, df);
087                    }
088                    return df;
089            }
090            
091            public static DateFormat[] getTimeFormats(Locale locale,TimeZone tz,boolean lenient) {
092                    String id="t-"+locale.hashCode()+"-"+tz.getID()+"-"+lenient;
093                    DateFormat[] df= formats.get(id);
094                    if(df==null) {
095                            List<DateFormat> list=new ArrayList<DateFormat>();
096                            list.add(DateFormat.getTimeInstance(DateFormat.FULL,locale));
097                            list.add(DateFormat.getTimeInstance(DateFormat.LONG,locale));
098                            list.add(DateFormat.getTimeInstance(DateFormat.MEDIUM,locale));
099                            list.add(DateFormat.getTimeInstance(DateFormat.SHORT,locale));
100                            add24(list, locale);
101                            addCustom(list, locale, FORMAT_TYPE_TIME);
102                            df=list.toArray(new DateFormat[list.size()]);
103                            
104                            for(int i=0;i<df.length;i++){
105                                     df[i].setLenient(lenient);
106                                     df[i].setTimeZone(tz);
107                            }
108                            formats.put(id, df);
109                    }
110                    return df;
111            }
112            
113    
114            private static void add24(List<DateFormat> list,Locale locale) {
115                    
116                    // if found h:mm:ss a add H:mm:ss ...
117                    String p;
118                    int index;
119                    SimpleDateFormat sdf;
120                    DateFormat[] df=list.toArray(new DateFormat[list.size()]);
121                    for(int i=0;i<df.length;i++){
122                            if(df[i] instanceof SimpleDateFormat) {
123                                    p=((SimpleDateFormat) df[i]).toPattern()+"";
124                                    
125                                    if(check(list,p,locale,"hh:mm:ss a","HH:mm:ss")) continue;
126                                    if(check(list,p,locale,"h:mm:ss a","H:mm:ss")) continue;
127                                    if(check(list,p,locale,"hh:mm a","HH:mm")) continue;
128                                    if(check(list,p,locale,"h:mm a","H:mm")) continue;
129                                    
130                                    if(check(list,p,locale,"hh:mm:ssa","HH:mm:ss")) continue;
131                                    if(check(list,p,locale,"h:mm:ssa","H:mm:ss")) continue;
132                                    if(check(list,p,locale,"hh:mma","HH:mm")) continue;
133                                    if(check(list,p,locale,"h:mma","H:mm")) continue;
134                                    
135                                    //if(check(list,p,locale,"HH:mm:ss","hh:mm:ss a")) continue;
136                                    //if(check(list,p,locale,"H:mm:ss","h:mm:ss a")) continue;
137                                    //if(check(list,p,locale,"HH:mm","hh:mm a")) continue;
138                                    //if(check(list,p,locale,"H:mm","h:mm a")) continue;
139                            }
140                    }
141            }
142            
143            private static boolean check(List<DateFormat> list, String p,Locale locale, String from, String to) {
144                    int index = p.indexOf(from);
145                    if(index!=-1) {
146                            p=StringUtil.replace(p, from, to, true);
147                            SimpleDateFormat sdf = new SimpleDateFormat(p,locale);
148                            if(!list.contains(sdf))list.add(sdf);
149                            return true;
150                    }
151                    return false;
152            }
153    
154    
155            private static void addCustom(List<DateFormat> list,Locale locale,short formatType) {
156                    // get custom formats from file
157                    Config config = ThreadLocalPageContext.getConfig();
158                    Resource dir=config.getConfigDir().getRealResource("locales");
159                    if(dir.isDirectory()) {
160                            String appendix="-datetime";
161                            if(formatType==FORMAT_TYPE_DATE)appendix="-date";
162                            if(formatType==FORMAT_TYPE_TIME)appendix="-time";
163                            
164                            Resource file = dir.getRealResource(locale.getLanguage()+"-"+locale.getCountry()+appendix+".df");
165                            if(file.isFile()) {
166                                    try {
167                                            String content=IOUtil.toString(file, null);
168                                            String[] arr = railo.runtime.type.util.ListUtil.listToStringArray(content, '\n');
169                                            String line;
170                                            SimpleDateFormat sdf;
171                                            for(int i=0;i<arr.length;i++){
172                                                    line=arr[i].trim();
173                                                    if(StringUtil.isEmpty(line)) continue;
174                                                    sdf = new SimpleDateFormat(line,locale);
175                                                    if(!list.contains(sdf))list.add(sdf);
176                                            }
177                                            
178                                    } 
179                                    catch (Throwable t) {}
180                            }
181                    }
182            }
183            
184            /**
185             * CFML Supported LS Formats
186             * @param locale
187             * @param tz
188             * @param lenient
189             * @return
190             */
191            public static DateFormat[] getCFMLFormats(TimeZone tz,boolean lenient) {
192                    String id="cfml-"+Locale.ENGLISH.hashCode()+"-"+tz.getID()+"-"+lenient;
193                    DateFormat[] df= formats.get(id);
194                    if(df==null) {
195                            df= new SimpleDateFormat[]{
196                                              new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy",Locale.ENGLISH)
197                                             ,new SimpleDateFormat("MMMM dd, yyyy HH:mm:ss a zzz",Locale.ENGLISH)
198                                             ,new SimpleDateFormat("MMM dd, yyyy HH:mm:ss a",Locale.ENGLISH)         
199                                             ,new SimpleDateFormat("MMM dd, yyyy HH:mm:ss",Locale.ENGLISH)   
200                                             ,new SimpleDateFormat("MMMM d yyyy HH:mm:ssZ",Locale.ENGLISH)
201                                             ,new SimpleDateFormat("MMMM d yyyy HH:mm:ss",Locale.ENGLISH)
202                                             ,new SimpleDateFormat("MMMM d yyyy HH:mm",Locale.ENGLISH)
203                                             ,new SimpleDateFormat("EEE, MMM dd, yyyy HH:mm:ssZ",Locale.ENGLISH)
204                                             ,new SimpleDateFormat("EEE, MMM dd, yyyy HH:mm:ss",Locale.ENGLISH)
205                                         ,new SimpleDateFormat("EEEE, MMMM dd, yyyy H:mm:ss a zzz",Locale.ENGLISH)
206                                             ,new SimpleDateFormat("dd-MMM-yy HH:mm a",Locale.ENGLISH)
207                                             ,new SimpleDateFormat("dd-MMMM-yy HH:mm a",Locale.ENGLISH)
208                                             ,new SimpleDateFormat("EE, dd MMM yyyy HH:mm:ss zz",Locale.ENGLISH)
209                                             ,new SimpleDateFormat("EEE d, MMM yyyy HH:mm:ss zz",Locale.ENGLISH)
210                                             ,new SimpleDateFormat("dd-MMM-yyyy",Locale.ENGLISH)
211                                             ,new SimpleDateFormat("MMMM, dd yyyy HH:mm:ssZ",Locale.ENGLISH)
212                                             ,new SimpleDateFormat("MMMM, dd yyyy HH:mm:ss",Locale.ENGLISH)
213                                             ,new SimpleDateFormat("yyyy/MM/dd HH:mm:ss zz",Locale.ENGLISH)
214                                             ,new SimpleDateFormat("dd MMM yyyy HH:mm:ss zz",Locale.ENGLISH)
215                                             ,new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss 'GMT'ZZ (z)",Locale.ENGLISH)
216                                             ,new SimpleDateFormat("dd MMM, yyyy HH:mm:ss",Locale.ENGLISH)
217                                             //,new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss",Locale.ENGLISH)
218                                    };
219                            
220                            for(int i=0;i<df.length;i++){
221                                    df[i].setLenient(lenient);
222                                    df[i].setTimeZone(tz);
223                            }
224                            formats.put(id, df);
225                    }
226                    return df;
227            }
228    
229            public static DateFormat[] getFormats(Locale locale,TimeZone tz,boolean lenient, short formatType) {
230                    if(FORMAT_TYPE_DATE_TIME==formatType)return getDateTimeFormats(locale,TimeZoneConstants.GMT,true);
231                    if(FORMAT_TYPE_DATE==formatType)return getDateFormats(locale,TimeZoneConstants.GMT,true);
232                    if(FORMAT_TYPE_TIME==formatType)return getTimeFormats(locale,TimeZoneConstants.GMT,true);
233                    
234                    DateFormat[] dt = getDateTimeFormats(locale,TimeZoneConstants.GMT,true);
235                    DateFormat[] d = getDateFormats(locale,TimeZoneConstants.GMT,true);
236                    DateFormat[] t = getTimeFormats(locale,TimeZoneConstants.GMT,true);
237                    
238                    DateFormat[] all=new DateFormat[dt.length+d.length+t.length];
239                    for(int i=0;i<dt.length;i++){
240                            all[i]=dt[i];
241                    }
242                    for(int i=0;i<d.length;i++){
243                            all[i+dt.length]=d[i];
244                    }
245                    for(int i=0;i<t.length;i++){
246                            all[i+dt.length+d.length]=t[i];
247                    }
248                    return getDateTimeFormats(locale,TimeZoneConstants.GMT,true);
249            }
250    
251            public static String[] getSupportedPatterns(Locale locale, short formatType) {
252                    DateFormat[] _formats = getFormats(locale,TimeZoneConstants.GMT,true,formatType);
253                    String[] patterns=new String[_formats.length];
254                    for(int i=0;i<_formats.length;i++){
255                            if(!(_formats[i] instanceof SimpleDateFormat))return null; // all or nothing
256                            patterns[i]=((SimpleDateFormat)_formats[i]).toPattern();
257                    }
258                    
259                    return patterns;
260            }
261            
262            
263            
264    }