001    package railo.runtime.op;
002    
003    import java.io.ByteArrayOutputStream;
004    import java.io.InputStream;
005    import java.sql.Blob;
006    import java.sql.Clob;
007    import java.text.DateFormat;
008    import java.text.ParseException;
009    import java.util.Calendar;
010    import java.util.Date;
011    import java.util.Iterator;
012    import java.util.List;
013    import java.util.Locale;
014    import java.util.Map;
015    import java.util.TimeZone;
016    import java.util.regex.Pattern;
017    
018    import org.w3c.dom.Document;
019    import org.w3c.dom.Element;
020    import org.w3c.dom.Node;
021    import org.w3c.dom.NodeList;
022    
023    import railo.commons.date.DateTimeUtil;
024    import railo.commons.date.JREDateTimeUtil;
025    import railo.commons.i18n.FormatUtil;
026    import railo.commons.lang.CFTypes;
027    import railo.commons.lang.StringUtil;
028    import railo.runtime.Component;
029    import railo.runtime.coder.Base64Util;
030    import railo.runtime.converter.WDDXConverter;
031    import railo.runtime.engine.ThreadLocalPageContext;
032    import railo.runtime.exp.ExpressionException;
033    import railo.runtime.exp.PageException;
034    import railo.runtime.ext.function.Function;
035    import railo.runtime.java.JavaObject;
036    import railo.runtime.net.mail.MailUtil;
037    import railo.runtime.op.date.DateCaster;
038    import railo.runtime.op.validators.ValidateCreditCard;
039    import railo.runtime.text.xml.XMLCaster;
040    import railo.runtime.text.xml.XMLUtil;
041    import railo.runtime.text.xml.struct.XMLStruct;
042    import railo.runtime.type.Array;
043    import railo.runtime.type.Closure;
044    import railo.runtime.type.Collection;
045    import railo.runtime.type.Collection.Key;
046    import railo.runtime.type.ObjectWrap;
047    import railo.runtime.type.Objects;
048    import railo.runtime.type.Query;
049    import railo.runtime.type.QueryColumn;
050    import railo.runtime.type.Struct;
051    import railo.runtime.type.UDF;
052    import railo.runtime.type.dt.DateTime;
053    
054    
055    /**
056     * Object to test if a Object is a specific type
057     */
058    public final class Decision {
059    
060            private static final String STRING_DEFAULT_VALUE = "this is a unique string";
061            
062            private static Pattern emailPattern; 
063            private static Pattern ssnPattern;
064            private static Pattern phonePattern;
065            private static Pattern urlPattern;
066            private static Pattern zipPattern; 
067    
068            /**
069             * tests if value is a simple value (Number,String,Boolean,Date,Printable)
070             * @param value value to test
071             * @return is value a simple value
072             */
073            public static boolean isSimpleValue(Object value){
074                    return 
075                                    (value instanceof Number) || 
076                                    (value instanceof String) || 
077                                    (value instanceof Boolean) || 
078                                    (value instanceof Date) || 
079                                    ((value instanceof Castable) && !(value instanceof Objects) && !(value instanceof Collection));
080            }
081            
082            /**
083             * tests if value is Numeric
084             * @param value value to test
085             * @return is value numeric
086             */
087            public static boolean isNumeric(Object value) {
088                    if(value instanceof Number) return true;
089                    else if(value instanceof String) {
090                            return isNumeric(value.toString());
091                    }
092                    
093                    else return false;
094            }
095    
096            public static boolean isCastableToNumeric(Object o) {
097                    
098                    if(isNumeric(o)) return true;
099                    else if(isBoolean(o)) return true;
100                    else if(isDateSimple(o,false)) return true;
101            else if(o == null) return true;
102            else if(o instanceof ObjectWrap) return isCastableToNumeric(((ObjectWrap)o).getEmbededObject("notanumber"));
103    
104            else if(o instanceof Castable) {
105                    return Decision.isValid(((Castable)o).castToDoubleValue(Double.NaN));
106                    
107            }
108                    return false;
109            }
110            
111            public static boolean isCastableToDate(Object o) {
112                    if(isDateAdvanced(o, true)) return true;
113                    else if(isBoolean(o)) return true;
114            
115            else if(o instanceof ObjectWrap) return isCastableToDate(((ObjectWrap)o).getEmbededObject("notadate"));
116    
117            else if(o instanceof Castable) {
118                    return ((Castable)o).castToDateTime(null)!=null;
119                    
120            }
121                    return false;
122            }
123    
124            /**
125             * tests if value is Numeric
126             * @param value value to test
127             * @return is value numeric
128             */
129            public static boolean isNumeric(Object value, boolean alsoBooleans) {
130                    if(alsoBooleans && isBoolean(value)) return true;
131                    return isNumeric(value);
132            }
133            
134            /**
135             * tests if String value is Numeric
136             * @param str value to test
137             * @return is value numeric
138             */
139            public static boolean isNumeric(String str) {
140            if(str==null) return false; 
141            str=str.trim();
142            
143            int pos=0; 
144            int len=str.length(); 
145            if(len==0) return false; 
146            char curr=str.charAt(pos); 
147            
148            if(curr=='+' || curr=='-') { 
149                    if(len==++pos) return false; 
150                    curr=str.charAt(pos); 
151            }
152            
153            boolean hasDot=false; 
154            boolean hasExp=false; 
155            for(;pos<len;pos++) { 
156                curr=str.charAt(pos); 
157                if(curr<'0') {
158                    if(curr=='.') { 
159                        if(pos+1>=len || hasDot) return false; 
160                        hasDot=true; 
161                    } 
162                    else return false;
163                }
164                else if(curr>'9') {
165                    if(curr=='e' || curr=='E') { 
166                        if(pos+1>=len || hasExp) return false; 
167                        hasExp=true; 
168                        hasDot=true; 
169                    }
170                    else return false;
171                }
172            } 
173            if(hasExp){
174                    try{
175                            Double.parseDouble(str);
176                            return true;
177                    }
178                    catch( NumberFormatException e){
179                            return false;
180                    } 
181            }
182            return true; 
183        } 
184    
185    
186            public static boolean isInteger(Object value) {
187                    return isInteger(value,false);          
188            }
189    
190            public static boolean isInteger(Object value,boolean alsoBooleans) {
191                    if(!alsoBooleans && value instanceof Boolean) return false;
192                    double dbl = Caster.toDoubleValue(value,Double.NaN);
193                    if(!Decision.isValid(dbl)) return false;
194                    int i=(int)dbl;
195                    return i==dbl;          
196            }
197    
198             /** tests if String value is Hex Value
199             * @param str value to test
200             * @return is value numeric
201             */
202            public static boolean isHex(String str) { 
203          if(str==null || str.length()==0) return false; 
204          
205          for(int i=str.length()-1;i>=0;i--) {
206              char c=str.charAt(i);
207              if(!(c>='0' && c<='9')) {
208                  c=Character.toLowerCase(c);
209                  if(!(c=='a' || c=='b' || c=='c' || c=='d' || c=='e' || c=='f'))return false;
210              }
211          }
212          return true;
213            }
214    
215            /** tests if String value is UUID Value
216             * @param obj value to test
217             * @return is value numeric
218             * @deprecated use instead <code>isUUId(Object obj)</code>
219             */
220            public static boolean isUUID(Object obj) { 
221                    return isUUId(obj);
222            }
223            
224             /** tests if String value is UUID Value
225             * @param obj value to test
226             * @return is value numeric
227             */
228            public static boolean isUUId(Object obj) { 
229                    String str=Caster.toString(obj,null);
230                    if(str==null) return false;
231    
232                    if(str.length()==35) {      
233                    return 
234                    Decision.isHex(str.substring(0,8)) &&
235                    str.charAt(8)=='-' &&
236                    Decision.isHex(str.substring(9,13)) &&
237                    str.charAt(13)=='-' &&
238                    Decision.isHex(str.substring(14,18)) &&
239                    str.charAt(18)=='-' &&
240                    Decision.isHex(str.substring(19));
241                }
242                    else if(str.length()==32)
243                            return Decision.isHex(str);
244                    return false;
245            }
246            
247    
248            /**
249             * @param obj
250             * @return
251             * @deprecated use instead <code>isGUId(Object)</code>
252             */
253            public static boolean isGUID(Object obj) { 
254                    return isGUId(obj);
255            }
256    
257            public static boolean isGUId(Object obj) { 
258                    String str=Caster.toString(obj,null);
259                    if(str==null) return false;
260    
261            
262                    // GUID
263                if(str.length()==36) {          
264                    return 
265                    Decision.isHex(str.substring(0,8)) &&
266                    str.charAt(8)=='-' &&
267                    Decision.isHex(str.substring(9,13)) &&
268                    str.charAt(13)=='-' &&
269                    Decision.isHex(str.substring(14,18)) &&
270                    str.charAt(18)=='-' &&
271                    Decision.isHex(str.substring(19,23)) &&
272                    str.charAt(23)=='-' &&
273                    Decision.isHex(str.substring(24));
274                }
275                return false;
276            }
277            
278    
279            public static boolean isGUIdSimple(Object obj) { 
280                    String str=Caster.toString(obj,null);
281                    if(str==null) return false;
282    
283            
284                    // GUID
285                if(str.length()==36) {          
286                    return 
287                    str.charAt(8)=='-' &&
288                    str.charAt(13)=='-' &&
289                    str.charAt(18)=='-' &&
290                    str.charAt(23)=='-';
291                }
292                return false;
293            }
294    
295            /**
296             * tests if value is a Boolean (Numbers are not acctepeted)
297             * @param value value to test
298             * @return is value boolean
299             */
300            public static boolean isBoolean(Object value) {
301            if(value instanceof Boolean) return true;
302            else if(value instanceof String) {
303                            return isBoolean(value.toString());
304                    }
305            else if(value instanceof ObjectWrap) return isBoolean(((ObjectWrap)value).getEmbededObject(null));
306                    else return false;
307            }
308    
309            public static boolean isCastableToBoolean(Object value) {
310                    if(value instanceof Boolean) return true;
311                    if(value instanceof Number) return true;
312            else if(value instanceof String) {
313                    String str = (String)value;
314                            return isBoolean(str) || isNumeric(str);
315                    }
316            else if(value instanceof Castable) {
317                return ((Castable)value).castToBoolean(null)!=null;
318                
319            }
320            else if(value instanceof ObjectWrap) return isCastableToBoolean(((ObjectWrap)value).getEmbededObject(null));
321                    else return false;
322            }
323            
324            public static boolean isBoolean(Object value, boolean alsoNumbers) {
325                    if(isBoolean(value)) return true;
326            else if(alsoNumbers) return isNumeric(value);
327            else return false;
328            }
329    
330        /**
331         * tests if value is a Boolean
332         * @param str value to test
333         * @return is value boolean
334         */
335        public static boolean isBoolean(String str) {
336            //str=str.trim();
337            if(str.length()<2) return false;
338            
339            switch(str.charAt(0)) {
340                case 't':
341                case 'T': return str.equalsIgnoreCase("true");
342                case 'f':
343                case 'F': return str.equalsIgnoreCase("false");
344                case 'y':
345                case 'Y': return str.equalsIgnoreCase("yes");
346                case 'n':
347                case 'N': return str.equalsIgnoreCase("no");
348            }
349            return false;
350        }   
351    
352            /**
353             * tests if value is DateTime Object
354             * @param value value to test
355             * @param alsoNumbers interpret also a number as date
356             * @return is value a DateTime Object
357             */
358        public static boolean isDate(Object value,boolean alsoNumbers) {
359            return isDateSimple(value, alsoNumbers);
360        }
361    
362            public static boolean isDateSimple(Object value,boolean alsoNumbers) {
363                    return isDateSimple(value, alsoNumbers, false);
364            }
365            public static boolean isDateSimple(Object value,boolean alsoNumbers, boolean alsoMonthString) {
366                    
367            //return DateCaster.toDateEL(value)!=null;
368                    if(value instanceof DateTime)           return true;
369                    else if(value instanceof Date)          return true;
370                    // wrong timezone but this isent importend because date will not be importend
371                    else if(value instanceof String)        return DateCaster.toDateSimple(value.toString(),alsoNumbers,alsoMonthString,TimeZone.getDefault(),null)!=null;
372                    else if(value instanceof ObjectWrap) {
373                    return isDateSimple(((ObjectWrap)value).getEmbededObject(null),alsoNumbers);
374            }
375            else if(value instanceof Castable)      {
376                        return ((Castable)value).castToDateTime(null)!=null;
377                
378                    }
379                    else if(alsoNumbers && value instanceof Number) return true;
380                    else if(value instanceof Calendar) return true;
381                    return false;
382            }
383            
384            public static boolean isDateAdvanced(Object value,boolean alsoNumbers) {
385                //return DateCaster.toDateEL(value)!=null;
386                    if(value instanceof DateTime)           return true;
387                    else if(value instanceof Date)          return true;
388                    // wrong timezone but this isent importend because date will not be importend
389                    else if(value instanceof String)        return DateCaster.toDateAdvanced(value.toString(),alsoNumbers,TimeZone.getDefault(),null)!=null;
390                    else if(value instanceof Castable)      {
391                        return ((Castable)value).castToDateTime(null)!=null;
392                 
393                    }
394                    else if(alsoNumbers && value instanceof Number) return true;
395                    else if(value instanceof ObjectWrap) {
396                    return isDateAdvanced(((ObjectWrap)value).getEmbededObject(null),alsoNumbers);
397            }
398            else if(value instanceof Calendar) return true;
399                    return false;
400            }
401            
402            private static char[] DATE_DEL=new char[]{'.','/','-'};
403            
404            public static boolean isUSDate(Object value) {
405                    String str = Caster.toString(value,"");
406                    return isUSorEuroDateEuro(str,false);
407            }
408            
409            public static boolean isUSDate(String str) {
410                    return isUSorEuroDateEuro(str,false);
411            }
412            
413            public static boolean isEuroDate(Object value) {
414                    String str = Caster.toString(value,"");
415                    return isUSorEuroDateEuro(str,true);
416            }
417            
418            public static boolean isEuroDate(String str) {
419                    return isUSorEuroDateEuro(str,true);
420            }
421            
422            private static boolean isUSorEuroDateEuro(String str, boolean isEuro) {
423                    if(StringUtil.isEmpty(str)) return false;
424                    
425                    for(int i=0;i<DATE_DEL.length;i++) {
426                            Array arr = railo.runtime.type.util.ListUtil.listToArrayRemoveEmpty(str,DATE_DEL[i]);
427                            if(arr.size()!=3) continue;
428    
429                            int month=Caster.toIntValue(    arr.get(isEuro?2:1,Constants.INTEGER_0),Integer.MIN_VALUE);
430                            int day=Caster.toIntValue(              arr.get(isEuro?1:2,Constants.INTEGER_0),Integer.MIN_VALUE);
431                            int year=Caster.toIntValue(             arr.get(3,Constants.INTEGER_0),Integer.MIN_VALUE);
432    
433                            
434                            if(month==Integer.MIN_VALUE) continue;
435                            if(month>12) continue;
436                            if(day==Integer.MIN_VALUE) continue;
437                            if(day>31) continue;
438                            if(year==Integer.MIN_VALUE) continue;
439                            if(DateTimeUtil.getInstance().toTime(null,year, month, day, 0, 0, 0,0, Long.MIN_VALUE)==Long.MIN_VALUE) continue;
440                            return true;
441                    }
442                    return false;
443            }
444            
445            public static boolean isCastableToStruct(Object o) {
446                    if(isStruct(o)) return true;
447                    if(o == null) return false;
448                    else if(o instanceof ObjectWrap) {
449                    if(o instanceof JavaObject ) return true;
450                return isCastableToStruct(((ObjectWrap)o).getEmbededObject(null));
451            }
452                    if(Decision.isSimpleValue(o)){
453                            return false;
454                    }
455                    //if(isArray(o) || isQuery(o)) return false;
456            return false;
457            }
458    
459            /**
460             * tests if object is a struct 
461             * @param o
462             * @return is struct or not
463             */
464            public static boolean isStruct(Object o) {
465                    if(o instanceof Struct) return true;
466            else if(o instanceof Map)return true;
467            else if(o instanceof Node)return true;
468                    return false;
469            }
470    
471            
472            /**
473             * can this type be casted to a array
474             * @param o
475             * @return
476             * @throws PageException
477             */
478            public static boolean isCastableToArray(Object o) {
479            if(isArray(o)) return true;
480            //else if(o instanceof XMLStruct) return true;
481            else if(o instanceof Struct) {
482                Struct sct=(Struct) o;
483                Iterator<Key> it = sct.keyIterator();
484                try {
485                    while(it.hasNext()) {
486                            Caster.toIntValue(it.next().getString());
487                    }
488                    return true;
489                } 
490                catch (Throwable t) {
491                    return false;
492                }
493            }
494            return false;
495        }
496            
497            /**
498             * tests if object is a array 
499             * @param o
500             * @return is array or not
501             */
502            public static boolean isArray(Object o) {
503                    if(o instanceof Array)                          return true;
504                    if(o instanceof List)                           return true;
505                    if(isNativeArray(o))                            return true;
506                    if(o instanceof ObjectWrap) {
507                return isArray(((ObjectWrap)o).getEmbededObject(null));
508            }
509            return false;
510            }
511    
512            /**
513             * tests if object is a native java array 
514             * @param o
515             * @return is a native (java) array
516             */
517            public static boolean isNativeArray(Object o) {
518                    //return o.getClass().isArray();
519                    if(o instanceof Object[])                       return true;
520                    else if(o instanceof boolean[])         return true;
521                    else if(o instanceof byte[])            return true;
522                    else if(o instanceof char[])            return true;
523                    else if(o instanceof short[])           return true;
524                    else if(o instanceof int[])                     return true;
525                    else if(o instanceof long[])            return true;
526                    else if(o instanceof float[])           return true;
527                    else if(o instanceof double[])          return true;
528                    return false;
529            }
530    
531            /**
532             * tests if object is catable to a binary  
533             * @param object
534             * @return boolean
535             */
536            public static boolean isCastableToBinary(Object object,boolean checkBase64String) {
537                    if(isBinary(object))return true;
538                    if(object instanceof InputStream) return true;
539                    if(object instanceof ByteArrayOutputStream) return true;
540                    if(object instanceof Blob) return true;
541            
542                    // Base64 String
543                    if(!checkBase64String) return false;
544                    String str = Caster.toString(object,null);
545            if(str==null) return false;
546            return Base64Util.isBase64(str);
547                    
548            }
549    
550            /**
551             * tests if object is a binary  
552             * @param object
553             * @return boolean
554             */
555            public static boolean isBinary(Object object) {
556                    if(object instanceof byte[]) return true;
557                    if(object instanceof ObjectWrap) return isBinary(((ObjectWrap)object).getEmbededObject(""));
558                    return false;
559            }
560    
561            /**
562             * tests if object is a Component  
563             * @param object
564             * @return boolean
565             */
566            public static boolean isComponent(Object object) {
567                    return object instanceof Component;
568            }
569    
570            /**
571             * tests if object is a Query  
572             * @param object
573             * @return boolean
574             */
575            public static boolean isQuery(Object object) {
576                    if(object instanceof Query)return true;
577                    else if(object instanceof ObjectWrap) {
578                return isQuery(((ObjectWrap)object).getEmbededObject(null));
579            }
580            return false;
581            }
582            public static boolean isQueryColumn(Object object) {
583                    if(object instanceof QueryColumn)return true;
584                    else if(object instanceof ObjectWrap) {
585                return isQueryColumn(((ObjectWrap)object).getEmbededObject(null));
586            }
587            return false;
588            }
589    
590            /**
591             * tests if object is a binary  
592             * @param object
593             * @return boolean
594             */
595            public static boolean isUserDefinedFunction(Object object) {
596                    return object instanceof UDF;
597            }
598            
599            /**
600             * tests if year is a leap year 
601             * @param year year to check
602             * @return boolean
603             */
604            public static final boolean isLeapYear(int year) {
605                    return DateTimeUtil.getInstance().isLeapYear(year);
606                    //return new GregorianCalendar().isLeapYear(year);
607        }
608            
609            /**
610             * tests if object is a WDDX Object 
611             * @param o Object to check
612             * @return boolean
613             */
614            public static boolean isWddx(Object o) {
615                    if(!(o instanceof String)) return false;
616                    String str=o.toString();
617                    if(!(str.indexOf("wddxPacket")>0)) return false;
618                    
619                    // wrong timezone but this isent importend because date will not be used
620                    WDDXConverter converter =new WDDXConverter(TimeZone.getDefault(),false,true);
621                    try {
622                            converter.deserialize(Caster.toString(o),true);
623                    } 
624                    catch (Exception e) {
625                            return false;
626                    }
627                    return true;
628            }
629            
630            /**
631             * tests if object is a XML Object 
632             * @param o Object to check
633             * @return boolean
634             */
635            public static boolean isXML(Object o) {
636                    if(o instanceof Node || o instanceof NodeList) return true;
637                    if(o instanceof ObjectWrap) {
638                return isXML(((ObjectWrap)o).getEmbededObject(null));
639            }
640            try {
641                XMLCaster.toXMLStruct(XMLUtil.parse(XMLUtil.toInputSource(null, o),null,false),false);
642                return true;
643            }
644            catch(Exception outer) {
645                return false;
646            }
647                    
648            }
649            
650            public static boolean isVoid(Object o) {
651                    if(o==null)return true;
652            else if(o instanceof String)    return o.toString().length()==0;
653            else if(o instanceof Number)    return ((Number)o).intValue()==0;
654            else if(o instanceof Boolean)   return ((Boolean)o).booleanValue()==false ;
655            else if(o instanceof ObjectWrap)return isVoid(((ObjectWrap)o).getEmbededObject(("isnotnull")));
656                    return false;
657            }
658            
659            
660            /**
661             * tests if object is a XML Element Object 
662             * @param o Object to check
663             * @return boolean
664             */
665            public static boolean isXMLElement(Object o) {
666                    return o instanceof Element;
667            }
668            
669            /**
670             * tests if object is a XML Document Object 
671             * @param o Object to check
672             * @return boolean
673             */
674            public static boolean isXMLDocument(Object o) {
675                    return o instanceof Document;
676            }
677            
678            /**
679             * tests if object is a XML Root Element Object 
680             * @param o Object to check
681             * @return boolean
682             */
683            public static boolean isXMLRootElement(Object o) {
684                    if(o instanceof Node) {
685                            Node n=(Node)o;
686                            if(n instanceof XMLStruct)n=((XMLStruct)n).toNode();
687                            return n.getOwnerDocument()!=null && n.getOwnerDocument().getDocumentElement()==n;
688                    }
689                    return false;
690            }
691    
692            /**
693             * @param obj
694             * @return returns if string represent a variable name
695             */
696            public static boolean isVariableName(Object obj) {
697                    if(obj instanceof String) return isVariableName((String)obj);
698                    return false;
699            }
700    
701            public static boolean isFunction(Object obj) {
702                    if(obj instanceof UDF)return true;
703                    else if(obj instanceof ObjectWrap) {
704                return isFunction(((ObjectWrap)obj).getEmbededObject(null));
705            }
706            return false;
707            }
708    
709            public static boolean isClosure(Object obj) {
710                    if(obj instanceof Closure)return true;
711                    else if(obj instanceof ObjectWrap) {
712                return isClosure(((ObjectWrap)obj).getEmbededObject(null));
713            }
714            return false;
715            }
716    
717            /**
718             * @param string
719             * @return returns if string represent a variable name
720             */
721            public static boolean isVariableName(String string) {
722                    if(string.length()==0)return false;
723                    int len=string.length();
724                    int pos=0;
725                    while(pos<len) {
726                        char first=string.charAt(pos);
727                        if(!((first>='a' && first<='z')||(first>='A' && first<='Z')||(first=='_')))
728                                    return false;
729                        pos++;
730                        for(;pos<len;pos++) {
731                                    char c=string.charAt(pos);
732                                    if(!((c>='a' && c<='z')||(c>='A' && c<='Z')||(c>='0' && c<='9')||(c=='_')))
733                                            break;
734                            }
735                        if(pos==len) return true;
736                        if(string.charAt(pos)=='.')pos++;
737                    }
738                    return false;
739            }
740            
741            /**
742             * @param string
743             * @return returns if string represent a variable name
744             */
745            public static boolean isSimpleVariableName(String string) {
746                    if(string.length()==0)return false;
747                    
748                    char first=string.charAt(0);
749                    if(!((first>='a' && first<='z')||(first>='A' && first<='Z')||(first=='_')))
750                            return false;
751                    for(int i=string.length()-1;i>0;i--) {
752                            char c=string.charAt(i);
753                            if(!((c>='a' && c<='z')||(c>='A' && c<='Z')||(c>='0' && c<='9')||(c=='_')))
754                                    return false;
755                    }       
756                    return true;
757            }
758            
759            /**
760             * @param key
761             * @return returns if string represent a variable name
762             */
763            public static boolean isSimpleVariableName(Collection.Key key) {
764                    String strKey = key.getLowerString();
765                    if(strKey.length()==0)return false;
766                    
767                    char first=strKey.charAt(0);
768                    if(!((first>='a' && first<='z')||(first=='_')))
769                            return false;
770                    for(int i=strKey.length()-1;i>0;i--) {
771                            char c=strKey.charAt(i);
772                            if(!((c>='a' && c<='z')||(c>='0' && c<='9')||(c=='_')))
773                                    return false;
774                    }       
775                    return true;
776            }
777    
778            /**
779             * returns if object is a CFML object
780             * @param o Object to check
781             * @return is or not
782             */
783            public static boolean isObject(Object o) {
784                    return isComponent(o)
785                    
786                            || (!isArray(o)
787                            && !isQuery(o)
788                            && !isSimpleValue(o)
789                            && !isStruct(o)
790                            && !isUserDefinedFunction(o)
791                            && !isXML(o));
792            }
793    
794        /**
795         * @param obj
796         * @return return if a String is "Empty", that means NULL or String with length 0 (whitespaces will not counted) 
797         */
798        public static boolean isEmpty(Object obj) {
799            if(obj instanceof String)return StringUtil.isEmpty((String)obj);
800            return obj==null;
801        }
802    
803    
804        /**
805         * @deprecated use instead <code>StringUtil.isEmpty(String)</code>
806         * @param str
807         * @return return if a String is "Empty", that means NULL or String with length 0 (whitespaces will not counted) 
808         */
809        public static boolean isEmpty(String str) {
810            return StringUtil.isEmpty(str);
811        }
812        
813        /**
814         * @deprecated use instead <code>StringUtil.isEmpty(String)</code>
815         * @param str
816         * @param trim 
817         * @return return if a String is "Empty", that means NULL or String with length 0 (whitespaces will not counted) 
818         */
819        public static boolean isEmpty(String str, boolean trim) {
820            return StringUtil.isEmpty(str,trim);
821        }
822        
823    
824            /**
825             * returns if a value is a credit card
826             * @param value
827             * @return is credit card
828             */
829            public static boolean isCreditCard(Object value) {
830                    return ValidateCreditCard.isValid(Caster.toString(value,"0"));
831            }
832            
833    
834            /**
835             * returns if given object is a email
836             * @param value
837             * @return
838             */
839            public static boolean isEmail(Object value) {
840    
841            return MailUtil.isValidEmail(value);
842            }       
843            
844            
845            
846            /**
847             * returns if given object is a social security number (usa)
848             * @param value
849             * @return
850             */
851            public static boolean isSSN(Object value) {
852                    String str = Caster.toString(value,null);
853                    if(str==null)return false;
854                    
855                    if(ssnPattern==null)
856                            ssnPattern=Pattern.compile("^[0-9]{3}[-|]{1}[0-9]{2}[-|]{1}[0-9]{4}$");
857                    
858                    return ssnPattern.matcher(str.trim()).matches();
859                    
860            }
861            
862            /**
863             * returns if given object is a phone
864             * @param value
865             * @return
866             */
867            public static boolean isPhone(Object value) {
868                    String str = Caster.toString(value,null);
869                    if(str==null)return false;
870                    
871                    if(phonePattern==null)
872                            phonePattern=Pattern.compile("^(\\+?1?[ \\-\\.]?([\\(]?([1-9][0-9]{2})[\\)]?))?[ ,\\-,\\.]?([^0-1]){1}([0-9]){2}[ ,\\-,\\.]?([0-9]){4}(( )((x){0,1}([0-9]){1,5}){0,1})?$");
873                    return phonePattern.matcher(str.trim()).matches();
874                    
875            }       
876    
877            /**
878             * returns if given object is a URL
879             * @param value
880             * @return
881             */
882            public static boolean isURL(Object value) {
883                    String str = Caster.toString(value,null);
884                    if(str==null)return false;
885                    
886                    if(urlPattern==null)
887                            urlPattern=Pattern.compile("^((http|https|ftp|file)\\:\\/\\/([a-zA-Z0-0]*:[a-zA-Z0-0]*(@))?[a-zA-Z0-9-\\.]+(\\.[a-zA-Z]{2,3})?(:[a-zA-Z0-9]*)?\\/?([a-zA-Z0-9-\\._\\? \\,\\'\\/\\+&amp;%\\$#\\=~])*)|((mailto)\\:[a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*@([a-zA-Z0-9-]+\\.)+[a-zA-Z0-9]{2,7})|((news)\\: [a-zA-Z0-9\\.]*)$");
888                    return urlPattern.matcher(str.trim()).matches();
889            }
890            
891            /**
892             * returns if given object is a zip code
893             * @param value
894             * @return
895             */
896            public static boolean isZipCode(Object value) {
897                    String str = Caster.toString(value,null);
898                    if(str==null)return false;
899                    
900                    if(zipPattern==null)
901                            zipPattern=Pattern.compile("([0-9]{5,5})|([0-9]{5,5}[- ]{1}[0-9]{4,4})");
902                    return zipPattern.matcher(str.trim()).matches();
903            }
904    
905            public static boolean isString(Object o) {
906                    if(o instanceof String)                 return true;
907            else if(o instanceof Boolean)   return true;
908            else if(o instanceof Number)    return true;
909            else if(o instanceof Date)              return true;
910            else if(o instanceof Castable) {
911                return ((Castable)o).castToString(STRING_DEFAULT_VALUE)!=STRING_DEFAULT_VALUE;
912                
913            }
914            else if(o instanceof Clob)              return true;
915            else if(o instanceof Node)              return true;
916            else if(o instanceof Map || o instanceof List || o instanceof Function) return false;
917            else if(o == null) return true;
918            else if(o instanceof ObjectWrap) return isString(((ObjectWrap)o).getEmbededObject(""));
919                    return true;
920            }
921            public static boolean isCastableToString(Object o) {
922                    return isString(o);
923            }
924            
925        
926        public static boolean isValid(String type, Object value) throws ExpressionException {
927            type=StringUtil.toLowerCase(type.trim());
928            char first = type.charAt(0);
929            switch(first) {
930            case 'a':
931                    if("any".equals(type))                  return true;//isSimpleValue(value);
932                    if("array".equals(type))                return isArray(value);
933            break;
934            case 'b':
935                    if("binary".equals(type))               return isBinary(value);
936                    if("boolean".equals(type))              return isBoolean(value,true);
937            break;
938            case 'c':
939                    if("creditcard".equals(type))   return isCreditCard(value);
940                    if("component".equals(type))    return isComponent(value);
941                    if("cfc".equals(type))                  return isComponent(value);
942            break;
943            case 'd':
944                    if("date".equals(type))                 return isDateAdvanced(value,true);  // ist zwar nicht logisch aber ident. zu Neo
945                    if("double".equals(type))               return isCastableToNumeric(value);
946            break;
947            case 'e':
948                    if("eurodate".equals(type))             return isEuroDate(value); 
949                    if("email".equals(type))                return isEmail(value);
950            break;
951            case 'f':
952                    if("float".equals(type))                return isNumeric(value,true);
953                    if("function".equals(type))             return isFunction(value);
954            break;
955            case 'g':
956                    if("guid".equals(type))                 return isGUId(value);
957            break;
958            case 'i':
959                    if("integer".equals(type))              return isInteger(value,false);
960            break;
961            case 'n':
962                    if("numeric".equals(type))              return isCastableToNumeric(value);
963                    if("number".equals(type))               return isCastableToNumeric(value);
964                    if("node".equals(type))                 return isXML(value); 
965            break;
966            case 'p':
967                    if("phone".equals(type))                return isPhone(value);
968            break;
969            case 'q':
970                    if("query".equals(type))                return isQuery(value);
971            break;
972            case 's':
973                    if("simple".equals(type))               return isSimpleValue(value);    
974                    if("struct".equals(type))               return isStruct(value);
975                    if("ssn".equals(type))                  return isSSN(value);
976                    if("social_security_number".equals(type))return isSSN(value);
977                    if("string".equals(type))               return isString(value);
978            break;
979            case 't':
980                    if("telephone".equals(type))    return isPhone(value);
981                    if("time".equals(type))                 return isDateAdvanced(value,false);
982            break;
983            case 'u':
984                    if("usdate".equals(type))               return isUSDate(value); 
985                    if("uuid".equals(type))                 return isUUId(value);
986                    if("url".equals(type))                  return isURL(value);
987            break;
988            case 'v':
989                    if("variablename".equals(type)) return isVariableName(Caster.toString(value,""));
990            break;
991            case 'x':
992                    if("xml".equals(type))  return isXML(value); // DIFF 23
993            break;
994            case 'z':
995                    if("zip".equals(type))                  return isZipCode(value);
996                    if("zipcode".equals(type))              return isZipCode(value);
997            break;
998            }
999            throw new ExpressionException("invalid type ["+type+"], valid types are [any,array,binary,boolean,component,creditcard,date,time,email,eurodate,float,numeric,guid,integer,query,simple,ssn,string,struct,telephone,URL,UUID,USdate,variableName,zipcode]");
1000                    
1001        }
1002        
1003        /**
1004         * checks if a value is castable to a certain type
1005         * @param type any,array,boolean,binary, ...
1006         * @param o value to check
1007         * @param alsoPattern also check patterns like creditcards,email,phone ...
1008         * @param maxlength only used for email,url, string, ignored otherwise
1009         * @return
1010         */
1011        public static boolean isCastableTo(String type, Object o, boolean alsoAlias, boolean alsoPattern, int maxlength) {
1012            
1013            type=StringUtil.toLowerCase(type).trim();
1014            if(type.length()>2) {
1015                char first=type.charAt(0);
1016                switch(first) {
1017                    case 'a':
1018                        if(type.equals("any")) {
1019                            return true;
1020                        }
1021                        else if(type.equals("array")) {
1022                            return isCastableToArray(o);
1023                        }
1024                        break;
1025                    case 'b':
1026                        if(type.equals("boolean") || (alsoAlias && type.equals("bool"))) {
1027                            return isCastableToBoolean(o);
1028                        }
1029                        else if(type.equals("binary")) {
1030                            return isCastableToBinary(o,true);
1031                        }
1032                        else if(alsoAlias && type.equals("bigint")) {
1033                            return isCastableToNumeric(o);
1034                        }
1035                        else if(type.equals("base64")) {
1036                            return Caster.toBase64(o,null,null)!=null;
1037                        }
1038                        break;
1039                    case 'c':
1040                            if(alsoPattern && type.equals("creditcard")) {
1041                            return Caster.toCreditCard(o,null)!=null;
1042                        }
1043                            if(alsoPattern && type.equals("char")) {
1044                                    if(maxlength>-1) {
1045                                    String str = Caster.toString(o,null);
1046                                    if(str==null) return false;
1047                                    return str.length()<=maxlength;
1048                            }
1049                            return isCastableToString(o);
1050                        }
1051                        break;
1052                    case 'd':
1053                            if(type.equals("date")) {
1054                            return isDateAdvanced(o, true);
1055                        }
1056                        else if(type.equals("datetime")) {
1057                            return isDateAdvanced(o, true);
1058                        }
1059                        else if(alsoAlias && type.equals("double")) {
1060                            return isCastableToNumeric(o);
1061                        }
1062                        else if(alsoAlias && type.equals("decimal")) {
1063                            return Caster.toDecimal(o,null)!=null;
1064                        }
1065                        break;
1066                    case 'e':
1067                        if(alsoAlias && type.equals("eurodate")) {
1068                            return isDateAdvanced(o, true);
1069                        }
1070                        else if(alsoPattern && type.equals("email")) {
1071                            if(maxlength>-1) {
1072                                    String str = Caster.toEmail(o,null);
1073                                    if(str==null) return false;
1074                                    return str.length()<=maxlength;
1075                            }
1076                            return Caster.toEmail(o,null)!= null;
1077                        }
1078                        break;
1079                    case 'f':
1080                        if(alsoAlias && type.equals("float")) {
1081                            return isCastableToNumeric(o);
1082                        }
1083                        if(type.equals("function")) {
1084                            return isFunction(o);
1085                        }
1086                        break;
1087                    case 'g':
1088                        if(type.equals("guid")) {
1089                            return isGUId(o);
1090                        }
1091                        break;
1092                    case 'i':
1093                        if(alsoAlias && (type.equals("integer") || type.equals("int"))) {
1094                            return isCastableToNumeric(o);
1095                        }
1096                        break;
1097                    case 'l':
1098                        if(alsoAlias && type.equals("long")) {
1099                            return isCastableToNumeric(o);
1100                        }
1101                        break;
1102                    case 'n':
1103                        if(type.equals("numeric")) {
1104                            return isCastableToNumeric(o);
1105                        }
1106                        else if(type.equals("number")) {
1107                            return isCastableToNumeric(o);
1108                        }
1109                        
1110                        if(alsoAlias) {
1111                                        if(type.equals("node")) return isXML(o);
1112                                        else if(type.equals("nvarchar") || type.equals("nchar")) {
1113                                            if(maxlength>-1) {
1114                                            String str = Caster.toString(o,null);
1115                                            if(str==null) return false;
1116                                            return str.length()<=maxlength;
1117                                    }
1118                                    return isCastableToString(o);
1119                                        }
1120                                }
1121                        
1122                        break;
1123                    case 'o':
1124                        if(type.equals("object")) {
1125                            return true;
1126                        }
1127                        else if(alsoAlias && type.equals("other")) {
1128                            return true;
1129                        }
1130                        break;
1131                    case 'p':
1132                        if(alsoPattern && type.equals("phone")) {
1133                            return Caster.toPhone(o,null)!=null;
1134                        }
1135                        break;
1136                    case 'q':
1137                        if(type.equals("query")) {
1138                            return isQuery(o);
1139                        }
1140                        
1141                                if(type.equals("querycolumn")) return isQueryColumn(o);
1142                                
1143                        break;
1144                    case 's':
1145                        if(type.equals("string")) {
1146                            if(maxlength>-1) {
1147                                    String str = Caster.toString(o,null);
1148                                    if(str==null) return false;
1149                                    return str.length()<=maxlength;
1150                            }
1151                            return isCastableToString(o);
1152                        }
1153                        else if(type.equals("struct")) {
1154                            return isCastableToStruct(o);
1155                        }
1156                        else if(alsoAlias && type.equals("short")) {
1157                            return isCastableToNumeric(o);
1158                        }
1159                        else if(alsoPattern && (type.equals("ssn") ||type.equals("social_security_number"))) {
1160                            return Caster.toSSN(o,null)!=null;
1161                        }
1162                        break;
1163                    case 't':
1164                        if(type.equals("timespan")) {
1165                            return Caster.toTimespan(o,null)!=null;
1166                        }
1167                        if(type.equals("time")) {
1168                            return isDateAdvanced(o, true);
1169                        }
1170                        if(alsoPattern && type.equals("telephone")) {
1171                            return Caster.toPhone(o,null)!=null;
1172                        }
1173                        
1174    
1175                                if(alsoAlias && type.equals("timestamp")) return isDateAdvanced(o, true);
1176                                if(alsoAlias && type.equals("text")) {
1177                                    if(maxlength>-1) {
1178                                    String str = Caster.toString(o,null);
1179                                    if(str==null) return false;
1180                                    return str.length()<=maxlength;
1181                            }
1182                            return isCastableToString(o);
1183                                }
1184                                
1185                    case 'u':
1186                        if(type.equals("uuid")) {
1187                            return isUUId(o);
1188                        }
1189                        if(alsoAlias && type.equals("usdate")) {
1190                            return isDateAdvanced(o, true);
1191                        }
1192                        if(alsoPattern && type.equals("url")) {
1193                            if(maxlength>-1) {
1194                                    String str = Caster.toURL(o,null);
1195                                    if(str==null) return false;
1196                                    return str.length()<=maxlength;
1197                            }
1198                            return Caster.toURL(o,null)!=null;
1199                        }
1200                        if(alsoAlias && type.equals("udf")) {
1201                            return isFunction(o);
1202                        }
1203                        break;
1204                    case 'v':
1205                        if(type.equals("variablename")) {
1206                            return isVariableName(o);
1207                        }
1208                        else if(type.equals("void")) {
1209                            return isVoid(o);//Caster.toVoid(o,Boolean.TRUE)!=Boolean.TRUE;
1210                        }
1211                        else if(alsoAlias && type.equals("variable_name")) {
1212                            return isVariableName(o);
1213                        }
1214                        else if(alsoAlias && type.equals("variable-name")) {
1215                            return isVariableName(o);
1216                        }
1217                        if(type.equals("varchar")) {
1218                            if(maxlength>-1) {
1219                                    String str = Caster.toString(o,null);
1220                                    if(str==null) return false;
1221                                    return str.length()<=maxlength;
1222                            }
1223                            return isCastableToString(o);
1224                        }
1225                        break;
1226                    case 'x':
1227                        if(type.equals("xml")) {
1228                            return isXML(o);
1229                        }
1230                        break;
1231                    case 'z':
1232                        if(alsoPattern && (type.equals("zip") || type.equals("zipcode"))) {
1233                            return Caster.toZip(o,null)!=null;
1234                        }
1235                    break;
1236               }
1237            }
1238            if(o instanceof Component) {
1239                Component comp=((Component)o);
1240                return comp.instanceOf(type);
1241            }
1242            if(isArrayType(type) && isArray(o)){
1243                    String t=type.substring(0,type.length()-2);
1244                    Array arr = Caster.toArray(o,null);
1245                    if(arr!=null){
1246                            Iterator<Object> it = arr.valueIterator();
1247                            while(it.hasNext()){
1248                                    if(!isCastableTo(t, it.next(), alsoAlias,alsoPattern,-1))
1249                                            return false;
1250                                    
1251                            }
1252                            return true;
1253                    }
1254                    
1255            }
1256                    return false;
1257        }
1258        
1259        
1260    
1261            private static boolean isArrayType(String type) {
1262                    return type.endsWith("[]");
1263            }
1264    
1265            public static boolean isCastableTo(short type,String strType, Object o) {
1266                    switch(type){
1267                    case CFTypes.TYPE_ANY:          return true;
1268                    case CFTypes.TYPE_STRING:               return isCastableToString(o);
1269            case CFTypes.TYPE_BOOLEAN:      return isCastableToBoolean(o);
1270            case CFTypes.TYPE_NUMERIC:      return isCastableToNumeric(o);
1271            case CFTypes.TYPE_STRUCT:       return isCastableToStruct(o);
1272            case CFTypes.TYPE_ARRAY:        return isCastableToArray(o);
1273            case CFTypes.TYPE_QUERY:        return isQuery(o);
1274            case CFTypes.TYPE_QUERY_COLUMN: return isQueryColumn(o);
1275            case CFTypes.TYPE_DATETIME:     return isDateAdvanced(o, true);
1276            case CFTypes.TYPE_VOID:         return isVoid(o);//Caster.toVoid(o,Boolean.TRUE)!=Boolean.TRUE;
1277            case CFTypes.TYPE_BINARY:       return isCastableToBinary(o,true);
1278            case CFTypes.TYPE_TIMESPAN:     return Caster.toTimespan(o,null)!=null;
1279            case CFTypes.TYPE_UUID:         return isUUId(o);
1280            case CFTypes.TYPE_GUID:         return isGUId(o);
1281            case CFTypes.TYPE_VARIABLE_NAME:return isVariableName(o);
1282            case CFTypes.TYPE_FUNCTION:             return isFunction(o);
1283            case CFTypes.TYPE_XML:          return isXML(o);
1284                    }
1285                    
1286            if(o instanceof Component) {
1287                    Component comp=((Component)o);
1288                return comp.instanceOf(strType);
1289            }
1290            if(isArrayType(strType) && isArray(o)){
1291                    String t=strType.substring(0,strType.length()-2);
1292                    Array arr = Caster.toArray(o,null);
1293                    if(arr!=null){
1294                            Iterator it = arr.valueIterator();
1295                            while(it.hasNext()){
1296                                    if(!isCastableTo(type,t, it.next()))
1297                                            return false;
1298                                    
1299                            }
1300                            return true;
1301                    }
1302                    
1303            }
1304            
1305                    return false;
1306            }
1307    
1308        public synchronized static boolean isDate(String str,Locale locale, TimeZone tz,boolean lenient) {
1309            str=str.trim();
1310            tz=ThreadLocalPageContext.getTimeZone(tz);
1311            DateFormat[] df;
1312    
1313            // get Calendar
1314            Calendar c=JREDateTimeUtil.getCalendar(locale);
1315            //synchronized(c){
1316                    // datetime
1317                    df=FormatUtil.getDateTimeFormats(locale,tz,false);//dfc[FORMATS_DATE_TIME];
1318                    for(int i=0;i<df.length;i++) {
1319                        try {
1320                            synchronized(c) {
1321                                    df[i].parse(str);
1322                                    return true;
1323                            }
1324                        }
1325                        catch (ParseException e) {}
1326                    }
1327                    // date
1328                    df=FormatUtil.getDateFormats(locale,tz,false);//dfc[FORMATS_DATE];
1329                    for(int i=0;i<df.length;i++) {
1330                        try {
1331                            df[i].setTimeZone(tz);
1332                            synchronized(c) {
1333                                    df[i].parse(str);
1334                                    return true;
1335                            }
1336                    }
1337                        catch (ParseException e) {}
1338                    }
1339                    
1340                    // time
1341                    df=FormatUtil.getTimeFormats(locale,tz,false);//dfc[FORMATS_TIME];
1342                    for(int i=0;i<df.length;i++) {
1343                        try {
1344                            df[i].setTimeZone(tz);
1345                            synchronized(c) {
1346                                    df[i].parse(str);
1347                                    return true;
1348                            }
1349                        } 
1350                        catch (ParseException e) {}
1351                    }
1352            //} 
1353            if(lenient) return isDateSimple(str, false);
1354            return false;
1355        }
1356    
1357            /**
1358             * Checks if number is valid (not infinity or NaN)
1359             * @param dbl
1360             * @return
1361             */
1362            public static boolean isValid(double dbl) {
1363                    return !Double.isNaN(dbl) && !Double.isInfinite(dbl);
1364            }
1365    }