001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.op;
020
021import java.io.IOException;
022import java.math.BigDecimal;
023import java.util.Calendar;
024import java.util.Date;
025import java.util.HashSet;
026import java.util.Iterator;
027import java.util.List;
028import java.util.Locale;
029import java.util.Map;
030import java.util.Set;
031
032import lucee.commons.date.DateTimeUtil;
033import lucee.commons.lang.ExceptionUtil;
034import lucee.runtime.Component;
035import lucee.runtime.PageContext;
036import lucee.runtime.exp.ExpressionException;
037import lucee.runtime.exp.PageException;
038import lucee.runtime.i18n.LocaleFactory;
039import lucee.runtime.interpreter.VariableInterpreter;
040import lucee.runtime.op.date.DateCaster;
041import lucee.runtime.type.Collection;
042import lucee.runtime.type.Collection.Key;
043import lucee.runtime.type.UDFPlus;
044import lucee.runtime.type.dt.DateTime;
045import lucee.runtime.type.dt.DateTimeImpl;
046import lucee.runtime.type.ref.VariableReference;
047import lucee.runtime.type.wrap.ListAsArray;
048import lucee.runtime.type.wrap.MapAsStruct;
049
050/**
051 * class to compare objects and primitive value types
052 * 
053 * 
054 */
055public final class Operator {
056        
057        private static final Object NULL = new Object();
058
059
060        /** 
061         * compares two Objects 
062         * @param left 
063         * @param right 
064         * @return different of objects as int
065         * @throws PageException
066         */ 
067        public static int compare(Object left, Object right) throws PageException { 
068                //print.dumpStack();
069                if(left instanceof String)                      return compare((String)left,right);
070                else if(left instanceof Number)         return compare(((Number)left).doubleValue(),right);
071                else if(left instanceof Boolean)        return compare(((Boolean)left).booleanValue(),right);
072                else if(left instanceof Date)           return compare((Date)left ,right);
073                else if(left instanceof Castable)       return compare(((Castable)left) ,right); 
074                else if(left instanceof Locale)         return compare(((Locale)left) ,right); 
075                else if(left==null)                             return compare("",right);
076                /*/NICE disabled at the moment left Comparable 
077                else if(left instanceof Comparable) { 
078                        return ((Comparable)left).compareTo(right);
079                } */
080                else if(left instanceof Character)      return compare( ((Character)left).toString() , right ); 
081                else if(left instanceof Calendar)       return compare( ((Calendar)left).getTime() , right ); 
082                else {
083                        return error(false,true); 
084                }
085        } 
086        
087        public static int compare(Locale left, Object right) throws PageException { 
088                if(right instanceof String)                     return compare(left,(String)right); 
089                else if(right instanceof Number)        return compare(left,Caster.toString(right)); 
090                else if(right instanceof Boolean)       return compare(left,Caster.toString(right)); 
091                else if(right instanceof Date)          return compare(left,Caster.toString(right)); 
092                else if(right instanceof Castable)      return compare(left,((Castable)right).castToString()); 
093                else if(right instanceof Locale)        return left.toString().compareTo(right.toString()); 
094                else if(right==null)                            return compare( left, "" ); 
095                else if(right instanceof Character)     return compare(left,((Character)right).toString()); 
096                else if(right instanceof Calendar)      return compare(left, Caster.toString(((Calendar)right).getTime())  ); 
097                else return error(false,true); 
098        } 
099        
100        public static int compare(Object left, Locale right) throws PageException { 
101                return -compare(right,left); 
102        }
103        
104        public static int compare(Locale left, String right) { 
105                Locale rightLocale = LocaleFactory.getLocale(right, null);
106                if(rightLocale==null) return LocaleFactory.toString(left).compareTo(right);
107                return left.toString().compareTo(rightLocale.toString());
108        }
109        
110        public static int compare(String left, Locale right) { 
111                return -compare(right,left);
112        }
113        
114        /** 
115         * compares a Object with a String 
116         * @param left 
117         * @param right 
118         * @return difference as int
119         * @throws PageException
120         */ 
121        public static int compare(Object left, String right) throws PageException { 
122                if(left instanceof String)                      return compare((String)left, right ); 
123                else if(left instanceof Number)         return compare( ((Number)left).doubleValue() , right ); 
124                else if(left instanceof Boolean)        return compare( ((Boolean)left).booleanValue(), right ); 
125                else if(left instanceof Date)           return compare( (Date)left , right ); 
126                else if(left instanceof Castable)       return ((Castable)left).compareTo(right ); 
127                else if(left instanceof Locale)         return compare( (Locale)left , right ); 
128                else if(left==null)                             return "".compareToIgnoreCase(right);
129                else if(left instanceof Character)      return compare( ((Character)left).toString() , right ); 
130                else if(left instanceof Calendar)       return compare( ((Calendar)left).getTime() , right ); 
131                
132                else return error(false,true);
133        } 
134
135        /** 
136         * compares a String with a Object 
137         * @param left 
138         * @param right 
139         * @return difference as int
140         * @throws PageException
141         */ 
142        public static int compare(String left, Object right) throws PageException { 
143                if(right instanceof String)                     return compare(left,(String)right); 
144                else if(right instanceof Number)        return compare(left,((Number)right).doubleValue()); 
145                else if(right instanceof Boolean)       return compare(left,((Boolean)right).booleanValue()?1:0); 
146                else if(right instanceof Date)          return compare(left,(Date)right); 
147                else if(right instanceof Castable)      return -((Castable)right).compareTo(left);//compare(left ,((Castable)right).castToString());
148                else if(right instanceof Locale)        return compare(left ,(Locale)right);
149                else if(right==null)                            return left.compareToIgnoreCase("");
150                else if(right instanceof Character)     return compare(left ,((Character)right).toString());
151                else if(right instanceof Calendar)      return compare(left, ((Calendar)right).getTime()  ); 
152                else return error(false,true);  
153        } 
154
155        /** 
156         * compares a Object with a double 
157         * @param left 
158         * @param right 
159         * @return difference as int
160         * @throws PageException
161         */ 
162        public static int compare(Object left, double right) throws PageException { 
163                if(left instanceof Number)                      return compare( ((Number)left).doubleValue() ,right ); 
164                else if(left instanceof String)         return compare( (String)left, right ); 
165                else if(left instanceof Boolean)        return compare( ((Boolean)left).booleanValue()?1D:0D , right ); 
166                else if(left instanceof Date)           return compare( ((Date)left) ,right); 
167                else if(left instanceof Castable)       return ((Castable)left).compareTo(right); 
168                //else if(left instanceof Castable)     return compare(((Castable)left).castToDoubleValue() , right ); 
169                else if(left instanceof Locale)         return compare( ((Locale)left), Caster.toString(right)); 
170                else if(left==null)                             return -1;
171                else if(left instanceof Character)      return compare(((Character)left).toString(),right);
172                else if(left instanceof Calendar)       return compare( ((Calendar)left).getTime() , right ); 
173                else {
174                        return error(false,true); 
175                }
176        } 
177
178        /** 
179         * compares a double with a Object 
180         * @param left 
181         * @param right 
182         * @return difference as int
183         * @throws PageException
184         */ 
185        public static int compare(double left, Object right) throws PageException { 
186                if(right instanceof Number)                     return compare(left,((Number)right).doubleValue()); 
187                else if(right instanceof String)        return compare(left,(String)right); 
188                else if(right instanceof Boolean)       return compare(left,((Boolean)right).booleanValue()?1D:0D); 
189                else if(right instanceof Date)          return compare(left,((Date)right)); 
190                else if(right instanceof Castable)      return -((Castable)right).compareTo(left);//compare(left ,((Castable)right).castToDoubleValue());
191                else if(right instanceof Locale)        return compare(Caster.toString(left) ,((Locale)right));
192                else if(right==null)                            return 1;
193                else if(right instanceof Character)     return compare(left ,((Character)right).toString());
194                else if(right instanceof Calendar)      return compare(left, ((Calendar)right).getTime() ); 
195                else return error(true,false);  
196        } 
197
198        
199        /** 
200         * compares a Object with a boolean 
201         * @param left 
202         * @param right 
203         * @return difference as int
204         * @throws PageException
205         */ 
206        public static int compare(Object left, boolean right) throws PageException { 
207                if(left instanceof Boolean)                     return compare(((Boolean)left).booleanValue(),right); 
208                else if(left instanceof String)         return compare((String)left,right); 
209                else if(left instanceof Number)         return compare(((Number)left).doubleValue(),right?1D:0D); 
210                else if(left instanceof Date)           return compare(((Date)left),right?1:0); 
211                else if(left instanceof Castable)       return ((Castable)left).compareTo(right );
212                else if(left instanceof Locale)         return compare(((Locale)left),Caster.toString(right)); 
213                else if(left==null)                             return -1;
214                else if(left instanceof Character)      return compare(((Character)left).toString(),right);
215                else if(left instanceof Calendar)       return compare( ((Calendar)left).getTime() , right?1:0 ); 
216                else return error(false,true);  
217        } 
218
219        /** 
220         * compares a boolean with a Object 
221         * @param left 
222         * @param right 
223         * @return difference as int
224         * @throws PageException
225         */ 
226        public static int compare(boolean left, Object right) throws PageException { 
227                if(right instanceof Boolean)            return compare(left,((Boolean)right).booleanValue()); 
228                else if(right instanceof String)        return compare(left?1:0,(String)right); 
229                else if(right instanceof Number)        return compare(left?1D:0D,((Number)right).doubleValue()); 
230                else if(right instanceof Date)          return compare(left?1:0,((Date)right)); 
231                else if(right instanceof Castable)      return -((Castable)right).compareTo(left);//compare(left ,((Castable)right).castToBooleanValue());
232                else if(right instanceof Locale)        return compare(Caster.toString(left),((Locale)right)); 
233                else if(right==null)                            return 1;
234                else if(right instanceof Character)     return compare(left ,((Character)right).toString());
235                else if(right instanceof Calendar)      return compare(left?1:0, ((Calendar)right).getTime()  ); 
236                else return error(true,false);  
237        }
238        
239        /** 
240         * compares a Object with a Date 
241         * @param left 
242         * @param right 
243         * @return difference as int
244         * @throws PageException
245         */ 
246        public static int compare(Object left, Date right) throws PageException { 
247                if(left instanceof String)                      return compare((String)left,right); 
248                else if(left instanceof Number)         return compare(((Number)left).doubleValue() ,right.getTime()/1000 ); 
249                else if(left instanceof Boolean)        return compare( ((Boolean)left).booleanValue()?1D:0D , right.getTime()/1000 ); 
250                else if(left instanceof Date)           return compare( ((Date)left) , right ); 
251                else if(left instanceof Castable)       return ((Castable)left).compareTo(Caster.toDatetime(right,null) );
252                else if(left instanceof Locale)         return compare( ((Locale)left) , Caster.toString(right)); 
253                else if(left==null)                             return compare("", right);
254                else if(left instanceof Character)      return compare(((Character)left).toString(),right);
255                else if(left instanceof Calendar)       return compare( ((Calendar)left).getTime() , right ); 
256                else return error(false,true);  
257        }  
258
259        /** 
260         * compares a Date with a Object 
261         * @param left 
262         * @param right 
263         * @return difference as int
264         * @throws PageException
265         */ 
266        public static int compare(Date left, Object right) throws PageException { 
267                if(right instanceof String)                     return compare(left,(String)right); 
268                else if(right instanceof Number)        return compare(left.getTime()/1000,((Number)right).doubleValue()); 
269                else if(right instanceof Boolean)       return compare(left.getTime()/1000,((Boolean)right).booleanValue()?1D:0D); 
270                else if(right instanceof Date)          return compare(left.getTime()/1000,((Date)right).getTime()/1000); 
271                else if(right instanceof Castable)      return -((Castable)right).compareTo(Caster.toDate(left,null));//compare(left ,(Date)((Castable)right).castToDateTime());
272                else if(right instanceof Locale)        return compare(Caster.toString(left),(Locale)right); 
273                else if(right==null)                            return compare(left,"");
274                else if(right instanceof Character)     return compare(left ,((Character)right).toString());
275                else if(right instanceof Calendar)      return compare(left.getTime()/1000, ((Calendar)right).getTime().getTime()/1000  ); 
276                else return error(true,false);  
277        }
278        
279        public static int compare(Castable left, Object right) throws PageException { 
280                if(right instanceof String)                     return left.compareTo((String)right); 
281                else if(right instanceof Number)        return left.compareTo(((Number)right).doubleValue()); 
282                else if(right instanceof Boolean)       return left.compareTo(((Boolean)right).booleanValue()?1d:0d);
283                else if(right instanceof Date)          return left.compareTo(Caster.toDate(right,null));
284                else if(right instanceof Castable)      return compare(left.castToString() , ((Castable)right).castToString() ); 
285                else if(right instanceof Locale)        return compare(left.castToString() , (Locale)right);
286                else if(right == null)                          return compare(left.castToString(), "" ); 
287                else if(right instanceof Character)     return left.compareTo(((Character)right).toString());
288                else if(right instanceof Calendar)      return left.compareTo(new DateTimeImpl(((Calendar)right).getTime()) ); 
289                else return error(true,false); 
290        }
291        
292        public static int compare(Object left, Castable right) throws PageException { 
293                return -compare(right,left); 
294        }
295                
296
297        /** 
298         * compares a String with a String 
299         * @param left 
300         * @param right 
301         * @return difference as int
302         */
303        public static int compare(String left, String right) { 
304                if(Decision.isNumeric(left)) {
305                        if(Decision.isNumeric(right)){
306                                // long numbers
307                                if(left.length()>9 || right.length()>9) {
308                                        try{
309                                                return new BigDecimal(left).compareTo(new BigDecimal(right));
310                                        }
311                                        catch(Throwable t){
312                                                ExceptionUtil.rethrowIfNecessary(t);
313                                        }
314                                }
315                                return compare(Caster.toDoubleValue(left,Double.NaN),Caster.toDoubleValue(right,Double.NaN));
316                        }
317                        
318                        return compare(Caster.toDoubleValue(left,Double.NaN),right);
319                }
320                if(Decision.isBoolean(left))
321                        return compare(Caster.toBooleanValue(left,false)?1D:0D,right);
322//               NICE Date compare, perhaps datetime to double
323                return left.compareToIgnoreCase(right); 
324        }
325
326    /** 
327     * compares a String with a double 
328     * @param left 
329     * @param right 
330     * @return difference as int
331     */ 
332    public static int compare(String left, double right) { 
333        if(Decision.isNumeric(left)) {
334            if(left.length()>9) {
335                try{
336                        return new BigDecimal(left).compareTo(new BigDecimal(right));
337                }
338                catch(Throwable t){
339                                ExceptionUtil.rethrowIfNecessary(t);
340                        }
341            }
342                return compare(Caster.toDoubleValue(left,Double.NaN),right); 
343        }
344        if(Decision.isBoolean(left))
345            return compare(Caster.toBooleanValue(left,false),right); 
346        
347        if(left.length()==0) return -1;
348        char leftFirst=left.charAt(0);
349        if(leftFirst>='0' && leftFirst<='9')
350            return left.compareToIgnoreCase(Caster.toString(right));
351        return leftFirst-'0';
352    }
353
354    /** 
355         * compares a String with a boolean 
356         * @param left 
357         * @param right 
358         * @return difference as int
359         */ 
360        public static int compare(String left, boolean right) { 
361                if(Decision.isBoolean(left))
362            return compare(Caster.toBooleanValue(left,false),right); 
363                if(Decision.isNumeric(left))
364            return compare(Caster.toDoubleValue(left,Double.NaN),right?1d:0d); 
365        
366        if(left.length()==0) return -1;
367        char leftFirst=left.charAt(0);
368        //print.ln(left+".compareTo("+Caster.toString(right)+")");
369        //p(left);
370        if(leftFirst>='0' && leftFirst<='9')
371            return left.compareToIgnoreCase(Caster.toString(right?1D:0D));
372        return leftFirst-'0';
373        } 
374
375        /** 
376         * compares a String with a Date 
377         * @param left 
378         * @param right 
379         * @return difference as int
380         * @throws PageException
381         */ 
382        public static int compare(String left, Date right) throws PageException { 
383                return -compare(right,left);
384        } 
385
386        /** 
387         * compares a double with a String 
388         * @param left 
389         * @param right 
390         * @return difference as int
391         */ 
392    public static int compare(double left, String right) { 
393        return -compare(right,left);
394    }
395    
396        /** 
397         * compares a double with a double 
398         * @param left 
399         * @param right 
400         * @return difference as int
401         */ 
402    public static int compare(double left, double right) { 
403        if((left)<(right))return -1; 
404        else if((left)>(right))return 1; 
405        else return 0;
406    }
407
408        /** 
409         * compares a double with a boolean 
410         * @param left 
411         * @param right 
412         * @return difference as int
413         */ 
414        public static int compare(double left, boolean right) { 
415                        return compare(left,right?1d:0d); 
416        } 
417
418        /** 
419         * compares a double with a Date 
420         * @param left 
421         * @param right 
422         * @return difference as int
423         */ 
424        public static int compare(double left, Date right) { 
425                        return compare(DateTimeUtil.getInstance().toDateTime(left).getTime()/1000,right.getTime()/1000); 
426        } 
427
428        /** 
429         * compares a boolean with a double 
430         * @param left 
431         * @param right 
432         * @return difference as int
433         */ 
434        public static int compare(boolean left, double right) { 
435                        return compare(left?1d:0d, right); 
436        } 
437
438        /** 
439         * compares a boolean with a double 
440         * @param left 
441         * @param right 
442         * @return difference as int
443         */ 
444        public static int compare(boolean left, String right) { 
445                        return -compare(right,left); 
446        } 
447
448        /** 
449         * compares a boolean with a boolean 
450         * @param left 
451         * @param right 
452         * @return difference as int
453         */ 
454        public static int compare(boolean left, boolean right) { 
455                        if(left)return right?0:1; 
456                        return right?-1:0; 
457        } 
458
459        /** 
460         * compares a boolean with a Date 
461         * @param left 
462         * @param right 
463         * @return difference as int
464         */ 
465        public static int compare(boolean left, Date right) { 
466                        return compare(left?1D:0D,right); 
467        } 
468
469        /** 
470         * compares a Date with a String 
471         * @param left 
472         * @param right 
473         * @return difference as int
474         * @throws PageException
475         */ 
476        public static int compare(Date left, String right) throws PageException { 
477                if(Decision.isNumeric(right)) return compare(left.getTime()/1000,Caster.toDoubleValue(right));
478                DateTime dt=DateCaster.toDateAdvanced(right,DateCaster.CONVERTING_TYPE_OFFSET,null,null);
479                if(dt!=null) {
480                        return compare(left.getTime()/1000,dt.getTime()/1000);          
481                }
482                return Caster.toString(left).compareToIgnoreCase(right);
483        } 
484
485        /** 
486         * compares a Date with a double 
487         * @param left 
488         * @param right 
489         * @return difference as int
490         */ 
491        public static int compare(Date left, double right) { 
492                        return compare(left.getTime()/1000, DateTimeUtil.getInstance().toDateTime(right).getTime()/1000); 
493        } 
494
495        /** 
496         * compares a Date with a boolean 
497         * @param left 
498         * @param right 
499         * @return difference as int
500         */ 
501        public static int compare(Date left, boolean right) { 
502                        return compare(left,right?1D:0D); 
503        } 
504
505        /** 
506         * compares a Date with a Date 
507         * @param left 
508         * @param right 
509         * @return difference as int
510         */ 
511        public static int compare(Date left, Date right) { 
512                        return compare(left.getTime()/1000,right.getTime()/1000); 
513        }        
514
515        private static int error(boolean leftIsOk, boolean rightIsOk) throws ExpressionException { 
516                // TODO remove this method
517                throw new ExpressionException("can't compare complex object types as simple value");
518        }
519
520        /**
521         * Method to compare to different values, return true of objects are same otherwise false
522         * @param left left value to compare
523         * @param right right value to compare
524         * @param caseSensitive check case sensitive  or not
525         * @return is same or not
526         * @throws PageException
527         */
528        public static boolean equals(Object left, Object right, boolean caseSensitive) throws PageException {
529                if(caseSensitive) {
530                        try {
531                                return Caster.toString(left).equals(Caster.toString(right));
532                        } catch (ExpressionException e) {
533                                return compare(left,right)==0;
534                        }
535                }
536                return compare(left,right)==0;
537        }
538        
539        public static boolean equalsEL(Object left, Object right, boolean caseSensitive, boolean allowComplexValues) {
540                if(!allowComplexValues || (Decision.isSimpleValue(left) && Decision.isSimpleValue(right))){
541                        try {
542                                return equals(left, right, caseSensitive);
543                        } catch (PageException e) {
544                                return false;
545                        }
546                }
547                return equalsComplexEL(left, right, caseSensitive,false);
548        }
549        
550        public static boolean equalsComplexEL(Object left, Object right, boolean caseSensitive, boolean checkOnlyPublicAppearance) {
551                return _equalsComplexEL(null,left, right, caseSensitive,checkOnlyPublicAppearance);
552        }
553        
554        public static boolean _equalsComplexEL(Set<Object> done,Object left, Object right, boolean caseSensitive, boolean checkOnlyPublicAppearance) {
555                if(left==right) return true;
556                if(Decision.isSimpleValue(left) && Decision.isSimpleValue(right)){
557                        try {
558                                return equals(left, right, caseSensitive);
559                        } catch (PageException e) {
560                                return false;
561                        }
562                }
563                if(left==null) return right==null;
564                
565                if(done==null)done=new HashSet<Object>();
566                else if(done.contains(left) && done.contains(right)) return true;
567                done.add(left);
568                done.add(right);
569                
570                if(left instanceof Component && right instanceof Component)
571                        return __equalsComplexEL(done,(Component)left, (Component)right,caseSensitive,checkOnlyPublicAppearance);
572                
573                if(left instanceof UDFPlus && right instanceof UDFPlus)
574                        return __equalsComplexEL(done,(UDFPlus)left, (UDFPlus)right,caseSensitive,checkOnlyPublicAppearance);
575                
576                if(left instanceof Collection && right instanceof Collection)
577                        return __equalsComplexEL(done,(Collection)left, (Collection)right,caseSensitive,checkOnlyPublicAppearance);
578                
579                if(left instanceof List && right instanceof List)
580                        return __equalsComplexEL(done,ListAsArray.toArray((List)left), ListAsArray.toArray((List)right),caseSensitive,checkOnlyPublicAppearance);
581                
582                if(left instanceof Map && right instanceof Map)
583                        return __equalsComplexEL(done,MapAsStruct.toStruct((Map)left,true), MapAsStruct.toStruct((Map)right,true),caseSensitive,checkOnlyPublicAppearance);
584                return left.equals(right);
585        }
586        
587        private static boolean __equalsComplexEL(Set<Object> done,UDFPlus left, UDFPlus right,boolean caseSensitive, boolean checkOnlyPublicAppearance) {
588                if(left==null || right==null) {
589                        if(left==right) return true;
590                        return false;
591                }
592                if(!left.getPageSource().equals(right.getPageSource())) return false;
593                if(left.getIndex()!=right.getIndex()) return false;
594                
595                return true;
596        }
597        
598        private static boolean __equalsComplexEL(Set<Object> done,Component left, Component right,boolean caseSensitive, boolean checkOnlyPublicAppearance) {
599                if(left==null || right==null) {
600                        if(left==right) return true;
601                        return false;
602                }
603                if(!left.getPageSource().equals(right.getPageSource())) return false;
604                if(!checkOnlyPublicAppearance && !__equalsComplexEL(done,left.getComponentScope(),right.getComponentScope(), caseSensitive,checkOnlyPublicAppearance)) return false;
605                if(!__equalsComplexEL(done,(Collection)left,(Collection)right, caseSensitive,checkOnlyPublicAppearance)) return false;
606                return true;
607        }
608        
609        private static boolean __equalsComplexEL(Set<Object> done,Collection left, Collection right,boolean caseSensitive, boolean checkOnlyPublicAppearance) {
610                if(left.size()!=right.size()) return false;
611                Iterator<Key> it = left.keyIterator();
612                Key k;
613                Object l,r;
614                while(it.hasNext()){
615                        k=it.next();
616                        l=left.get(k,NULL);
617                        r=right.get(k,NULL);
618                        if(l==NULL || r==NULL) {
619                                if(l==r) continue;
620                                return false;
621                        }
622                        
623                        if(!_equalsComplexEL(done,r, l, caseSensitive,checkOnlyPublicAppearance)) {
624                                return false;
625                        }
626                }
627                return true;
628        }
629        
630        
631        public static boolean equals(Object left, Object right, boolean caseSensitive, boolean allowComplexValues) throws PageException {
632                if(!allowComplexValues || (Decision.isSimpleValue(left) && Decision.isSimpleValue(right)))
633                        return equals(left, right, caseSensitive);
634                return equalsComplex(left, right, caseSensitive);
635        }
636
637        public static boolean equalsComplex(Object left, Object right, boolean caseSensitive) throws PageException {
638                return _equalsComplex(null,left, right, caseSensitive);
639        }
640        
641
642        public static boolean _equalsComplex(Set<Object> done,Object left, Object right, boolean caseSensitive) throws PageException {
643                if(Decision.isSimpleValue(left) && Decision.isSimpleValue(right)){
644                        return equals(left, right, caseSensitive);
645                }
646                if(left==null) return right==null;
647                if(done==null)done=new HashSet<Object>();
648                else if(done.contains(left) && done.contains(right)) return true;
649                done.add(left);
650                done.add(right);
651                
652                if(left instanceof Collection && right instanceof Collection)
653                        return __equalsComplex(done,(Collection)left, (Collection)right,caseSensitive);
654                
655                if(left instanceof List && right instanceof List)
656                        return __equalsComplex(done,ListAsArray.toArray((List)left), ListAsArray.toArray((List)right),caseSensitive);
657                
658                if(left instanceof Map && right instanceof Map)
659                        return __equalsComplex(done,MapAsStruct.toStruct((Map)left,true), MapAsStruct.toStruct((Map)right,true),caseSensitive);
660                
661                return left.equals(right);
662        }
663        
664        private static boolean __equalsComplex(Set<Object> done,Collection left, Collection right,boolean caseSensitive) throws PageException {
665                if(left.size()!=right.size()) return false;
666                Iterator<Key> it = left.keyIterator();
667                Key k;
668                Object l,r;
669                while(it.hasNext()){
670                        k=it.next();
671                        r=right.get(k,NULL);
672                        if(r==NULL) return false;
673                        l=left.get(k,NULL);
674                        if(!_equalsComplex(done,r, l, caseSensitive)) return false;
675                }
676                return true;
677        }
678        
679        /**
680         * check if left is inside right (String-> ignore case)
681         * @param left string to check
682         * @param right substring to find in string
683         * @return return if substring has been found
684         * @throws PageException
685         */
686        public static boolean ct(Object left, Object right) throws PageException {
687                return Caster.toString(left).toLowerCase().indexOf(Caster.toString(right).toLowerCase())!=-1;           
688        } 
689
690        /**
691         * Equivalence: Return True if both operands are True or both are False. The EQV operator is the opposite of the XOR operator. For example, True EQV True is True, but True EQV False is False.
692         * @param left value to check
693         * @param right value to check
694         * @return result of operation
695         * @throws PageException
696         */
697        public static boolean eqv(Object left, Object right) throws PageException {
698                return eqv(Caster.toBooleanValue(left),Caster.toBooleanValue(right));   
699        }
700
701        /**
702         * Equivalence: Return True if both operands are True or both are False. The EQV operator is the opposite of the XOR operator. For example, True EQV True is True, but True EQV False is False.
703         * @param left value to check
704         * @param right value to check
705         * @return result of operation
706         */
707        public static boolean eqv(boolean left, boolean right) {
708                return (left==true && right==true) || (left==false && right==false);    
709        }
710
711        /**
712         * Implication: The statement A IMP B is the equivalent of the logical statement 
713         * "If A Then B." A IMP B is False only if A is True and B is False. It is True in all other cases.
714         * @param left value to check
715         * @param right value to check
716         * @return result
717         * @throws PageException
718         */
719        public static boolean imp(Object left, Object right) throws PageException {
720                return imp(Caster.toBooleanValue(left),Caster.toBooleanValue(right));   
721        } 
722
723        /**
724         * Implication: The statement A IMP B is the equivalent of the logical statement 
725         * "If A Then B." A IMP B is False only if A is True and B is False. It is True in all other cases.
726         * @param left value to check
727         * @param right value to check
728         * @return result
729         */
730        public static boolean imp(boolean left, boolean right) {
731                return !(left==true && right==false);   
732        } 
733
734        /**
735         * check if left is not inside right (String-> ignore case)
736         * @param left string to check
737         * @param right substring to find in string
738         * @return return if substring NOT has been found
739         * @throws PageException
740         */
741        public static boolean nct(Object left, Object right) throws PageException {
742                return !ct(left,right);         
743        }
744
745
746        /**
747         * simple reference compersion
748         * @param left
749         * @param right
750         * @return
751         * @throws PageException
752         */
753        public static boolean eeq(Object left, Object right) throws PageException {
754                return left==right;             
755        }
756
757
758        /**
759         * simple reference compersion
760         * @param left
761         * @param right
762         * @return
763         * @throws PageException
764         */
765        public static boolean neeq(Object left, Object right) throws PageException {
766                return left!=right;             
767        }
768        
769        /**
770         * calculate the exponent of the left value 
771         * @param left value to get exponent from
772         * @param right exponent count
773         * @return return expoinended value
774         * @throws PageException
775         */
776        public static double exponent(Object left, Object right) throws PageException {
777                return StrictMath.pow(Caster.toDoubleValue(left),Caster.toDoubleValue(right));
778        } 
779        
780        public static double exponent(double left, double right) {
781                return StrictMath.pow(left,right);
782        } 
783        
784        public static double intdiv(double left, double right) {
785                return ((int)left)/((int)right);
786        } 
787        
788        public static double div(double left, double right) {
789                if(right==0d)
790                        throw new ArithmeticException("Division by zero is not possible");
791                return left/right;
792        } 
793        
794        public static float exponent(float left, float right) {
795                return (float) StrictMath.pow(left,right);
796        } 
797    
798
799    /**
800     * concat 2 CharSequences
801     * @param left
802     * @param right
803     * @return concated String
804     */
805        public static CharSequence concat(CharSequence left, CharSequence right) {
806                if(left instanceof Appendable) {
807                        try {
808                                ((Appendable)left).append(right);
809                                return left;
810                        } catch (IOException e) {}
811                }
812                return new StringBuilder(left).append(right);
813        }
814
815    /**
816     * plus operation
817     * @param left
818     * @param right
819     * @return result of the opertions
820     */
821    public final static double plus(double left, double right) {
822        return left+right;
823    }
824    
825    /**
826     * minus operation
827     * @param left
828     * @param right
829     * @return result of the opertions
830     */
831    public static double minus(double left, double right) {
832        return left-right;
833    }
834    
835    /**
836     * modulus operation
837     * @param left
838     * @param right
839     * @return result of the opertions
840     */
841    public static double modulus(double left, double right) {
842        return left%right;
843    }
844    
845    /**
846     * divide operation
847     * @param left
848     * @param right
849     * @return result of the opertions
850     */
851    public static double divide(double left, double right) {
852        return left/right;
853    }
854    
855    /**
856     * multiply operation
857     * @param left
858     * @param right
859     * @return result of the opertions
860     */
861    public static double multiply(double left, double right) {
862        return left*right;
863    }
864
865    /**
866     * bitand operation
867     * @param left
868     * @param right
869     * @return result of the opertions
870     */
871    public static double bitand(double left, double right) {
872        return (int)left&(int)right;
873    }
874
875    /**
876     * bitand operation
877     * @param left
878     * @param right
879     * @return result of the opertions
880     */
881    public static double bitor(double left, double right) {
882        return (int)left|(int)right;
883    }
884    
885
886    public static Double divRef(Object left, Object right) throws PageException {
887                double r = Caster.toDoubleValue(right);
888        if(r==0d)
889                        throw new ArithmeticException("Division by zero is not possible");
890                return Caster.toDouble(Caster.toDoubleValue(left)/r);
891        }
892    
893    public static Double exponentRef(Object left, Object right) throws PageException {
894                return Caster.toDouble(StrictMath.pow(Caster.toDoubleValue(left),Caster.toDoubleValue(right)));
895        }
896    
897    public static Double intdivRef(Object left, Object right) throws PageException {
898                return Caster.toDouble(Caster.toIntValue(left)/Caster.toIntValue(right));
899        }
900    
901    public static Double plusRef(Object left, Object right) throws PageException {
902                return Caster.toDouble(Caster.toDoubleValue(left)+Caster.toDoubleValue(right));
903        }
904    
905    public static Double minusRef(Object left, Object right) throws PageException {
906                return Caster.toDouble(Caster.toDoubleValue(left)-Caster.toDoubleValue(right));
907        }
908    
909    public static Double modulusRef(Object left, Object right) throws PageException {
910                double rightAsDouble = Caster.toDoubleValue(right);
911                if(rightAsDouble==0d)
912                        throw new ArithmeticException("Division by zero is not possible");
913                return Caster.toDouble(Caster.toDoubleValue(left)%rightAsDouble);
914        }
915    
916    public static Double divideRef(Object left, Object right) throws PageException {
917                return Caster.toDouble(Caster.toDoubleValue(left)/Caster.toDoubleValue(right));
918        }
919    
920    public static Double multiplyRef(Object left, Object right) throws PageException {
921                return Caster.toDouble(Caster.toDoubleValue(left)*Caster.toDoubleValue(right));
922        }
923
924// post plus
925    public static Double unaryPostPlus(PageContext pc,Collection.Key[] keys,double value) throws PageException {
926        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
927        double rtn=Caster.toDoubleValue(ref.get(pc));
928        ref.set(rtn+value);
929                return rtn;
930        }
931    public static Double unaryPostPlus(Collection coll,Collection.Key key,double value) throws PageException {
932        double rtn = Caster.toDoubleValue(coll.get(key));
933        coll.set(key, rtn+value);
934        return rtn;
935        }
936    public static double unaryPoPl(PageContext pc,Collection.Key[] keys,double value) throws PageException {
937        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
938        double rtn=Caster.toDoubleValue(ref.get(pc));
939        ref.set(rtn+value);
940                return rtn;
941        }
942    public static double unaryPoPl(PageContext pc,Collection.Key key,double value) throws PageException {
943        return unaryPoPl(pc.undefinedScope(),key,value);
944        }
945    public static double unaryPoPl(Collection coll,Collection.Key key,double value) throws PageException {
946        double rtn = Caster.toDoubleValue(coll.get(key));
947        coll.set(key, rtn+value);
948        return rtn;
949        }
950    
951    
952
953// post minus
954    public static Double unaryPostMinus(PageContext pc,Collection.Key[] keys,double value) throws PageException {
955        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
956        double rtn=Caster.toDoubleValue(ref.get(pc));
957        ref.set(rtn-value);
958                return rtn;
959        }
960    public static Double unaryPostMinus(Collection coll,Collection.Key key,double value) throws PageException {
961        double rtn = Caster.toDoubleValue(coll.get(key));
962        coll.set(key, rtn-value);
963        return rtn;
964        }
965    public static double unaryPoMi(PageContext pc,Collection.Key[] keys,double value) throws PageException {
966        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
967        double rtn=Caster.toDoubleValue(ref.get(pc));
968        ref.set(rtn-value);
969                return rtn;
970        }
971    public static double unaryPoMi(PageContext pc,Collection.Key key,double value) throws PageException {
972        return unaryPoMi(pc.undefinedScope(),key,value);
973        }
974    public static double unaryPoMi(Collection coll,Collection.Key key,double value) throws PageException {
975        double rtn = Caster.toDoubleValue(coll.get(key));
976        coll.set(key, rtn-value);
977        return rtn;
978        }
979    
980// pre plus
981    public static Double unaryPrePlus(PageContext pc,Collection.Key[] keys,double value) throws PageException {
982        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
983        double rtn=Caster.toDoubleValue(ref.get(pc))+value;
984        ref.set(rtn);
985                return rtn;
986        }
987    public static Double unaryPrePlus(Collection coll,Collection.Key key,double value) throws PageException {
988        double rtn = Caster.toDoubleValue(coll.get(key))+value;
989        coll.set(key, rtn);
990        return rtn;
991        }
992    public static double unaryPrPl(PageContext pc,Collection.Key[] keys,double value) throws PageException {
993        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
994        double rtn=Caster.toDoubleValue(ref.get(pc))+value;
995        ref.set(rtn);
996                return rtn;
997        }
998    public static double unaryPrPl(PageContext pc,Collection.Key key,double value) throws PageException {
999        return unaryPrPl(pc.undefinedScope(),key,value);
1000        }
1001    public static double unaryPrPl(Collection coll,Collection.Key key,double value) throws PageException {
1002        double rtn = Caster.toDoubleValue(coll.get(key))+value;
1003        coll.set(key, rtn);
1004        return rtn;
1005        }
1006
1007// pre minus
1008    public static Double unaryPreMinus(PageContext pc,Collection.Key[] keys,double value) throws PageException {
1009        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
1010        double rtn=Caster.toDoubleValue(ref.get(pc))-value;
1011        ref.set(rtn);
1012                return rtn;
1013        }
1014    public static Double unaryPreMinus(Collection coll,Collection.Key key,double value) throws PageException {
1015        double rtn = Caster.toDoubleValue(coll.get(key))-value;
1016        coll.set(key, rtn);
1017        return rtn;
1018        }
1019    public static double unaryPrMi(PageContext pc,Collection.Key[] keys,double value) throws PageException {
1020        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
1021        double rtn=Caster.toDoubleValue(ref.get(pc))-value;
1022        ref.set(rtn);
1023                return rtn;
1024        }
1025    public static double unaryPrMi(PageContext pc,Collection.Key key,double value) throws PageException {
1026        return unaryPrMi(pc.undefinedScope(),key,value);
1027        }
1028    public static double unaryPrMi(Collection coll,Collection.Key key,double value) throws PageException {
1029        double rtn = Caster.toDoubleValue(coll.get(key))-value;
1030        coll.set(key, rtn);
1031        return rtn;
1032        }
1033    
1034// pre multiply
1035    public static Double unaryPreMultiply(PageContext pc,Collection.Key[] keys,double value) throws PageException {
1036        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
1037        double rtn=Caster.toDoubleValue(ref.get(pc))*value;
1038        ref.set(rtn);
1039                return rtn;
1040        }
1041    public static Double unaryPreMultiply(Collection coll,Collection.Key key,double value) throws PageException {
1042        double rtn = Caster.toDoubleValue(coll.get(key))*value;
1043        coll.set(key, rtn);
1044        return rtn;
1045        }
1046    public static double unaryPrMu(PageContext pc,Collection.Key[] keys,double value) throws PageException {
1047        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
1048        double rtn=Caster.toDoubleValue(ref.get(pc))*value;
1049        ref.set(rtn);
1050                return rtn;
1051        }
1052    public static double unaryPrMu(PageContext pc,Collection.Key key,double value) throws PageException {
1053        return unaryPrMu(pc.undefinedScope(),key,value);
1054        }
1055    public static double unaryPrMu(Collection coll,Collection.Key key,double value) throws PageException {
1056        double rtn = Caster.toDoubleValue(coll.get(key))*value;
1057        coll.set(key, rtn);
1058        return rtn;
1059        }
1060
1061// pre divide    
1062    public static Double unaryPreDivide(PageContext pc,Collection.Key[] keys,double value) throws PageException {
1063        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
1064        double rtn=Caster.toDoubleValue(ref.get(pc))/value;
1065        ref.set(rtn);
1066                return rtn;
1067        }
1068    public static Double unaryPreDivide(Collection coll,Collection.Key key,double value) throws PageException {
1069        double rtn = Caster.toDoubleValue(coll.get(key))/value;
1070        coll.set(key, rtn);
1071        return rtn;
1072        }    
1073    public static double unaryPrDi(PageContext pc,Collection.Key[] keys,double value) throws PageException {
1074        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
1075        double rtn=Caster.toDoubleValue(ref.get(pc))/value;
1076        ref.set(rtn);
1077                return rtn;
1078        }    
1079    public static double unaryPrDi(PageContext pc,Collection.Key key,double value) throws PageException {
1080        return unaryPrDi(pc.undefinedScope(),key,value);
1081        }
1082    public static double unaryPrDi(Collection coll,Collection.Key key,double value) throws PageException {
1083        double rtn = Caster.toDoubleValue(coll.get(key))/value;
1084        coll.set(key, rtn);
1085        return rtn;
1086        } 
1087    
1088//Concat
1089    public static String unaryPreConcat(PageContext pc,Collection.Key[] keys,String value) throws PageException {
1090        VariableReference ref = VariableInterpreter.getVariableReference(pc, keys,true);
1091        String rtn=Caster.toString(ref.get(pc)).concat(value);
1092        ref.set(pc,rtn);
1093                return rtn;
1094        }
1095    public static String unaryPreConcat(Collection coll,Collection.Key key,String value) throws PageException {
1096        String rtn = Caster.toString(coll.get(key)).concat(value);
1097        coll.set(key, rtn);
1098        return rtn;
1099        }
1100    public static String unaryPreConcat(PageContext pc,Collection.Key key,String value) throws PageException {
1101        VariableReference ref = VariableInterpreter.getVariableReference(pc, key,true);
1102        String rtn=Caster.toString(ref.get(pc)).concat(value);
1103        ref.set(pc,rtn);
1104                return rtn;
1105        }
1106}