001 package railo.runtime.type; 002 003 import java.util.ArrayList; 004 import java.util.Date; 005 import java.util.Iterator; 006 import java.util.List; 007 import java.util.Map.Entry; 008 009 import railo.commons.lang.SizeOf; 010 import railo.runtime.PageContext; 011 import railo.runtime.config.NullSupportHelper; 012 import railo.runtime.dump.DumpData; 013 import railo.runtime.dump.DumpProperties; 014 import railo.runtime.dump.DumpUtil; 015 import railo.runtime.engine.ThreadLocalPageContext; 016 import railo.runtime.exp.DatabaseException; 017 import railo.runtime.exp.DeprecatedException; 018 import railo.runtime.exp.ExpressionException; 019 import railo.runtime.exp.PageException; 020 import railo.runtime.exp.PageRuntimeException; 021 import railo.runtime.op.Caster; 022 import railo.runtime.op.Duplicator; 023 import railo.runtime.op.Operator; 024 import railo.runtime.op.ThreadLocalDuplication; 025 import railo.runtime.op.date.DateCaster; 026 import railo.runtime.reflection.Reflector; 027 import railo.runtime.reflection.pairs.MethodInstance; 028 import railo.runtime.type.dt.DateTime; 029 import railo.runtime.type.it.EntryIterator; 030 import railo.runtime.type.it.KeyIterator; 031 import railo.runtime.type.it.StringIterator; 032 import railo.runtime.type.scope.Undefined; 033 import railo.runtime.type.util.CollectionUtil; 034 import railo.runtime.type.util.QueryUtil; 035 import railo.runtime.util.ArrayIterator; 036 037 /** 038 * implementation of the query column 039 */ 040 public class QueryColumnImpl implements QueryColumnPro,Sizeable,Objects { 041 042 private static final long serialVersionUID = -5544446523204021493L; 043 private static final int CAPACITY=32; 044 045 protected int type; 046 protected int size; 047 protected Object[] data; 048 049 protected boolean typeChecked=false; 050 protected QueryImpl query; 051 protected Collection.Key key; 052 053 /** 054 * constructor with type 055 * @param query 056 * @param key 057 * @param type 058 */ 059 public QueryColumnImpl(QueryImpl query, Collection.Key key, int type) { 060 this.data=new Object[CAPACITY]; 061 this.type=type; 062 this.key=key; 063 this.query=query; 064 } 065 066 /** 067 * constructor with array 068 * @param query 069 * @param array 070 * @param type 071 */ 072 public QueryColumnImpl(QueryImpl query, Collection.Key key, Array array,int type) { 073 data=array.toArray(); 074 size=array.size(); 075 this.type=type; 076 this.query=query; 077 this.key=key; 078 } 079 080 /** 081 * @param query 082 * @param type type as (java.sql.Types.XYZ) int 083 * @param size 084 */ 085 public QueryColumnImpl(QueryImpl query, Collection.Key key, int type, int size) { 086 this.data=new Object[size]; 087 this.type=type; 088 this.size=size; 089 this.query=query; 090 this.key=key; 091 } 092 093 094 /** 095 * Constructor of the class 096 * for internal usage only 097 */ 098 public QueryColumnImpl() { 099 } 100 101 @Override 102 public int size() { 103 return size; 104 } 105 106 @Override 107 public Collection.Key[] keys() { 108 Collection.Key[] k=new Collection.Key[size()]; 109 int len=k.length; 110 for(int i=1;i<=len;i++) { 111 k[i-1]=KeyImpl.init(Caster.toString(i)); 112 } 113 return k; 114 } 115 116 @Override 117 public Object remove(Collection.Key key) throws PageException { 118 resetType(); 119 return set(Caster.toIntValue(key.getString()),""); 120 } 121 122 @Override 123 public Object remove(int row) throws DatabaseException { 124 // query.disconnectCache(); 125 resetType(); 126 return set(row,""); 127 } 128 129 130 @Override 131 public Object removeEL(Collection.Key key) { 132 // query.disconnectCache(); 133 resetType(); 134 return setEL(Caster.toIntValue(key.getString(),-1),""); 135 } 136 137 @Override 138 public Object removeEL(int row) { 139 // query.disconnectCache(); 140 resetType(); 141 return setEL(row,""); 142 } 143 144 @Override 145 public synchronized void clear() { 146 // query.disconnectCache(); 147 resetType(); 148 data=new Object[CAPACITY]; 149 size=0; 150 } 151 152 @Override 153 public Object remove(PageContext pc) throws PageException { 154 return remove(query.getCurrentrow(pc.getId())); 155 } 156 157 public Object removeEL(PageContext pc) { 158 return removeEL(query.getCurrentrow(pc.getId())); 159 } 160 161 @Override 162 public Object get(String key) throws PageException { 163 return get(KeyImpl.init(key)); 164 } 165 166 @Override 167 public Object get(Key key) throws PageException { 168 return get((PageContext)null,key); 169 } 170 171 @Override 172 public Object get(PageContext pc, Collection.Key key) throws PageException { 173 int row=Caster.toIntValue(key.getString(),Integer.MIN_VALUE); 174 if(row==Integer.MIN_VALUE) { 175 Object child=getChildElement(pc,key,NullSupportHelper.NULL()); 176 if(child!=NullSupportHelper.NULL()) return child; 177 throw new DatabaseException("key ["+key+"] not found",null,null,null); 178 } 179 return QueryUtil.getValue(this,row); 180 } 181 182 private Object getChildElement(PageContext pc,Key key, Object defaultValue) {// pc maybe null 183 // column and query has same name 184 if(key.equals(this.key)) { 185 return query.get(key,defaultValue); 186 } 187 // get it from undefined scope 188 pc = ThreadLocalPageContext.get(pc); 189 if(pc!=null){ 190 Undefined undefined = pc.undefinedScope(); 191 boolean old = undefined.setAllowImplicidQueryCall(false); 192 Object sister = undefined.get(this.key,NullSupportHelper.NULL()); 193 undefined.setAllowImplicidQueryCall(old); 194 if(sister!=NullSupportHelper.NULL()){ 195 try { 196 return pc.get(sister, key); 197 } catch (PageException e) { 198 return defaultValue; 199 } 200 } 201 } 202 return defaultValue; 203 } 204 205 /** 206 * touch the given line on the column at given row 207 * @param row 208 * @return new row or existing 209 * @throws DatabaseException 210 */ 211 public Object touch(int row) { 212 if(row<1 || row>size) return NullSupportHelper.full()?null:""; 213 Object o=data[row-1]; 214 if(o!=null) return o; 215 return setEL(row,new StructImpl()); 216 } 217 218 /** 219 * touch the given line on the column at given row 220 * @param row 221 * @return new row or existing 222 * @throws DatabaseException 223 */ 224 public Object touchEL(int row) { 225 return touch(row); 226 } 227 228 @Override 229 public Object get(Key key, Object defaultValue) { 230 return get(null,key,defaultValue); 231 } 232 233 @Override 234 public Object get(PageContext pc, Collection.Key key, Object defaultValue) {// pc maybe null 235 int row=Caster.toIntValue(key.getString(),Integer.MIN_VALUE); 236 if(row==Integer.MIN_VALUE) { 237 return getChildElement(pc,key, defaultValue); 238 } 239 return get(row,defaultValue); 240 } 241 242 @Override 243 public Object get(String key, Object defaultValue) { 244 return get(KeyImpl.init(key),defaultValue); 245 } 246 247 @Override 248 public Object get(int row) throws DeprecatedException { 249 throw new DeprecatedException("this method is no longer supported, use instead get(int,Object)"); 250 //return QueryUtil.getValue(this,row); 251 } 252 253 public Object get(int row, Object defaultValue) { 254 if(row<1 || row>size) return defaultValue; 255 return data[row-1]; 256 } 257 258 // FUTURE this method should replace the method above, but it needs adjustment with all callers 259 /*public Object get2(int row, Object defaultValue) { 260 if(row<1 || row>size) return defaultValue; 261 return data[row-1]; 262 }*/ 263 264 @Override 265 public Object set(String key, Object value) throws PageException { 266 int row=Caster.toIntValue(key,Integer.MIN_VALUE); 267 if(row==Integer.MIN_VALUE)return query.set(key,value); 268 return set(row,value); 269 } 270 271 public Object set(Collection.Key key, Object value) throws PageException { 272 int row=Caster.toIntValue(key.getString(),Integer.MIN_VALUE); 273 if(row==Integer.MIN_VALUE)return query.set(key,value); 274 return set(row,value); 275 } 276 277 @Override 278 public synchronized Object set(int row, Object value) throws DatabaseException { 279 // query.disconnectCache(); 280 if(row<1) throw new DatabaseException("invalid row number ["+row+"]","valid row numbers a greater or equal to one",null,null,null); 281 if(row>size) { 282 if(size==0)throw new DatabaseException("cannot set a value to a empty query, you first have to add a row",null,null,null,null); 283 throw new DatabaseException("invalid row number ["+row+"]","valid row numbers goes from 1 to "+size,null,null,null); 284 } 285 286 value=reDefineType(value); 287 data[row-1]=value; 288 return value; 289 } 290 @Override 291 public synchronized Object setEL(String key, Object value) { 292 int index=Caster.toIntValue(key,Integer.MIN_VALUE); 293 if(index==Integer.MIN_VALUE) query.setEL(key,value); 294 return setEL(index, value); 295 296 } 297 298 public Object setEL(Collection.Key key, Object value) { 299 int index=Caster.toIntValue(key.getString(),Integer.MIN_VALUE); 300 if(index==Integer.MIN_VALUE) query.setEL(key,value); 301 return setEL(index, value); 302 } 303 304 @Override 305 public synchronized Object setEL(int row, Object value) { 306 // query.disconnectCache(); 307 if(row<1 || row>size) return value; 308 309 value=reDefineType(value); 310 data[row-1]=value; 311 return value; 312 } 313 314 @Override 315 public synchronized void add(Object value) { 316 // query.disconnectCache(); 317 if(data.length<=size) growTo(size); 318 data[size++]=value; 319 } 320 321 @Override 322 public synchronized void cutRowsTo(int maxrows) { 323 // query.disconnectCache(); 324 if(maxrows>-1 && maxrows<size)size=maxrows; 325 } 326 327 @Override 328 public synchronized void addRow(int count) { 329 // query.disconnectCache(); 330 if(data.length<(size+count)) growTo(size+count); 331 for(int i=0;i<count;i++)size++; 332 } 333 334 public synchronized Object removeRow(int row) throws DatabaseException { 335 // query.disconnectCache(); 336 if(row<1 || row>size) 337 throw new DatabaseException("invalid row number ["+row+"]","valid rows goes from 1 to "+size,null,null,null); 338 Object o=data[row-1]; 339 for(int i=row;i<size;i++) { 340 data[i-1]=data[i]; 341 } 342 size--; 343 if(NullSupportHelper.full()) return o; 344 return o==null?"":o; 345 } 346 347 @Override 348 public int getType() { 349 reOrganizeType(); 350 return type; 351 } 352 353 354 @Override 355 public String getTypeAsString() { 356 return QueryImpl.getColumTypeName(getType()); 357 } 358 359 360 @Override 361 public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { 362 return DumpUtil.toDumpData(QueryUtil.getValue(this,query.getCurrentrow(pageContext.getId())), pageContext,maxlevel,dp); 363 } 364 365 private synchronized void growTo(int row) { 366 367 int newSize=(data.length+1)*2; 368 while(newSize<=row) { 369 //print.ln(newSize+"<="+row); 370 newSize*=2; 371 } 372 373 Object[] newData=new Object[newSize]; 374 for(int i=0;i<data.length;i++) { 375 newData[i]=data[i]; 376 } 377 data=newData; 378 } 379 380 private Object reDefineType(Object value) { 381 return QueryColumnUtil.reDefineType(this,value); 382 } 383 384 private synchronized void resetType() { 385 QueryColumnUtil.resetType(this); 386 } 387 388 private synchronized void reOrganizeType() { 389 QueryColumnUtil.reOrganizeType(this); 390 } 391 392 public Collection.Key getKey() { 393 return key; 394 } 395 public void setKey(Collection.Key key) { 396 this.key = key; 397 } 398 399 @Override 400 public String getKeyAsString() throws PageException { 401 return key.getLowerString();// TODO ist das OK? 402 } 403 404 @Override 405 public Object get(PageContext pc) { 406 return QueryUtil.getValue(this,query.getCurrentrow(pc.getId())); 407 } 408 409 @Override 410 public Object get(PageContext pc, Object defaultValue) { 411 return get(query.getCurrentrow(pc.getId()),defaultValue); 412 } 413 414 @Override 415 public Object touch(PageContext pc) throws PageException { 416 return touch(query.getCurrentrow(pc.getId())); 417 } 418 419 public Object touchEL(PageContext pc) { 420 return touchEL(query.getCurrentrow(pc.getId())); 421 } 422 423 @Override 424 public Object set(PageContext pc, Object value) throws PageException { 425 return set(query.getCurrentrow(pc.getId()),value); 426 } 427 428 @Override 429 public Object setEL(PageContext pc, Object value) { 430 return setEL(query.getCurrentrow(pc.getId()),value); 431 } 432 433 @Override 434 public Object getParent() { 435 return query; 436 } 437 438 @Override 439 public String castToString() throws PageException { 440 return Caster.toString(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null)); 441 } 442 443 @Override 444 public String castToString(String defaultValue) { 445 Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL()); 446 if(value==NullSupportHelper.NULL()) return defaultValue; 447 return Caster.toString(value,defaultValue); 448 } 449 450 @Override 451 public boolean castToBooleanValue() throws PageException { 452 return Caster.toBooleanValue(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null)); 453 } 454 455 @Override 456 public Boolean castToBoolean(Boolean defaultValue) { 457 Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL()); 458 if(value==NullSupportHelper.NULL()) return defaultValue; 459 return Caster.toBoolean(value,defaultValue); 460 } 461 462 @Override 463 public double castToDoubleValue() throws PageException { 464 return Caster.toDoubleValue(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null)); 465 } 466 467 @Override 468 public double castToDoubleValue(double defaultValue) { 469 Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL()); 470 if(value==NullSupportHelper.NULL()) return defaultValue; 471 return Caster.toDoubleValue(value,defaultValue); 472 } 473 474 @Override 475 public DateTime castToDateTime() throws PageException { 476 return DateCaster.toDateAdvanced(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null),null); 477 } 478 479 @Override 480 public DateTime castToDateTime(DateTime defaultValue) { 481 Object value = get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),NullSupportHelper.NULL()); 482 if(value==NullSupportHelper.NULL()) return defaultValue; 483 return DateCaster.toDateAdvanced(value,true,null,defaultValue); 484 } 485 486 487 @Override 488 public int compareTo(boolean b) throws PageException { 489 return Operator.compare(castToBooleanValue(), b); 490 } 491 492 @Override 493 public int compareTo(DateTime dt) throws PageException { 494 return Operator.compare((Date)castToDateTime(), (Date)dt); 495 } 496 497 @Override 498 public int compareTo(double d) throws PageException { 499 return Operator.compare(castToDoubleValue(), d); 500 } 501 502 @Override 503 public int compareTo(String str) throws PageException { 504 return Operator.compare(castToString(), str); 505 } 506 507 @Override 508 public synchronized Object clone() { 509 return duplicate(true); 510 } 511 512 public synchronized Collection duplicate(boolean deepCopy) { 513 return cloneColumn(query,deepCopy); 514 } 515 516 public synchronized QueryColumnPro cloneColumn(Query query, boolean deepCopy) { 517 return cloneColumnImpl(deepCopy); 518 } 519 520 public synchronized QueryColumnImpl cloneColumnImpl(boolean deepCopy) { 521 QueryColumnImpl clone=new QueryColumnImpl(); 522 populate(this, clone, deepCopy); 523 return clone; 524 } 525 526 protected static void populate(QueryColumnImpl src,QueryColumnImpl trg, boolean deepCopy) { 527 528 ThreadLocalDuplication.set(src, trg); 529 try{ 530 trg.key=src.key; 531 trg.query=src.query; 532 trg.size=src.size; 533 trg.type=src.type; 534 trg.key=src.key; 535 536 trg.data=new Object[src.data.length]; 537 for(int i=0;i<src.data.length;i++) { 538 trg.data[i]=deepCopy?Duplicator.duplicate(src.data[i],true):src.data[i]; 539 } 540 } 541 finally { 542 // ThreadLocalDuplication.remove(src); removed "remove" to catch sisters and brothers 543 } 544 } 545 546 547 @Override 548 public String toString() { 549 try { 550 return Caster.toString(get(query.getCurrentrow(ThreadLocalPageContext.get().getId()),null)); 551 } catch (PageException e) { 552 return super.toString(); 553 } 554 } 555 556 @Override 557 public boolean containsKey(String key) { 558 return containsKey(KeyImpl.init(key)); 559 } 560 561 @Override 562 public boolean containsKey(Collection.Key key) { 563 return get(key,NullSupportHelper.NULL())!=NullSupportHelper.NULL(); 564 } 565 566 @Override 567 public long sizeOf() { 568 return SizeOf.size(key)+SizeOf.size(data); 569 } 570 571 public Iterator iterator() { 572 return keyIterator(); 573 } 574 575 @Override 576 public Iterator<Collection.Key> keyIterator() { 577 return new KeyIterator(keys()); 578 } 579 580 @Override 581 public Iterator<String> keysAsStringIterator() { 582 return new StringIterator(keys()); 583 } 584 585 @Override 586 public Iterator<Entry<Key, Object>> entryIterator() { 587 return new EntryIterator(this, keys()); 588 } 589 590 @Override 591 public Iterator<Object> valueIterator() { 592 return new ArrayIterator(data,0,size); 593 } 594 595 @Override 596 public Object callWithNamedValues(PageContext pc, Key methodName,Struct args) throws PageException { 597 598 throw new ExpressionException("No matching Method/Function ["+methodName+"] for call with named arguments found"); 599 //return pc.getFunctionWithNamedValues(get(query.getCurrentrow()), methodName, Caster.toFunctionValues(args)); 600 } 601 602 @Override 603 public Object call(PageContext pc, Key methodName, Object[] arguments) throws PageException { 604 MethodInstance mi = Reflector.getMethodInstanceEL(this.getClass(), methodName, arguments); 605 if(mi!=null) { 606 try { 607 return mi.invoke(this); 608 } catch (Throwable t) { 609 try { 610 return pc.getFunction(QueryUtil.getValue(this,query.getCurrentrow(pc.getId())), methodName, arguments); 611 } catch (PageException pe) { 612 throw Caster.toPageException(t); 613 } 614 } 615 } 616 return pc.getFunction(QueryUtil.getValue(this,query.getCurrentrow(pc.getId())), methodName, arguments); 617 } 618 619 @Override 620 public Object set(PageContext pc, Key propertyName, Object value) throws PageException { 621 return set(propertyName, value); 622 } 623 624 @Override 625 public Object setEL(PageContext pc, Key propertyName, Object value) { 626 return setEL(propertyName, value); 627 } 628 629 public void add(int index, Object element) { 630 throwNotAllowedToAlter(); 631 //setEL(index+1, element); 632 } 633 634 private void throwNotAllowedToAlter() { 635 throw new PageRuntimeException(new DatabaseException( 636 "Query columns do not support methods that would alter the structure of a query column" 637 ,"you must use an analogous method on the query" 638 ,null 639 ,null 640 ,null)); 641 642 } 643 644 public boolean addAll(java.util.Collection<? extends Object> c) { 645 throwNotAllowedToAlter(); 646 return false; 647 /*Iterator<? extends Object> it = c.iterator(); 648 while(it.hasNext()){ 649 add(it.next()); 650 } 651 return true;*/ 652 } 653 654 public boolean addAll(int index, java.util.Collection<? extends Object> c) { 655 throwNotAllowedToAlter(); 656 return false; 657 /*Iterator<? extends Object> it = c.iterator(); 658 while(it.hasNext()){ 659 setEL(++index,it.next()); 660 } 661 return true;*/ 662 } 663 664 public boolean contains(Object o) { 665 return indexOf(o)!=-1; 666 } 667 668 public boolean containsAll(java.util.Collection<?> c) { 669 Iterator<? extends Object> it = c.iterator(); 670 while(it.hasNext()){ 671 if(indexOf(it.next())==-1) return false; 672 } 673 return true; 674 } 675 676 public int indexOf(Object o) { 677 for(int i=0;i<size;i++){ 678 try { 679 if(Operator.compare(o, data[i])==0) return i; 680 } 681 catch (PageException e) {} 682 } 683 return -1; 684 } 685 686 public int lastIndexOf(Object o) { 687 for(int i=size-1;i>=0;i--){ 688 try { 689 if(Operator.compare(o, data[i])==0) return i; 690 } 691 catch (PageException e) {} 692 } 693 return -1; 694 } 695 696 public boolean isEmpty() { 697 return size()==0; 698 } 699 700 public boolean removeAll(java.util.Collection<?> c) { 701 throwNotAllowedToAlter(); 702 return false; 703 /*boolean hasChanged=false; 704 Iterator<? extends Object> it = c.iterator(); 705 while(it.hasNext()){ 706 if(remove(it.next())) { 707 hasChanged=true; 708 } 709 } 710 return hasChanged;*/ 711 } 712 713 public boolean retainAll(java.util.Collection<?> c) { 714 throwNotAllowedToAlter(); 715 return false; 716 /*boolean hasChanged=false; 717 Iterator it = valueIterator(); 718 while(it.hasNext()){ 719 if(!c.contains(it.next())){ 720 hasChanged=true; 721 it.remove(); 722 } 723 } 724 return hasChanged;*/ 725 } 726 727 public List<Object> subList(int fromIndex, int toIndex) { 728 ArrayList<Object> list=new ArrayList<Object>(); 729 for(int i=fromIndex;i<toIndex;i++){ 730 list.add(data[i]); 731 } 732 return list; 733 } 734 735 public Object[] toArray() { 736 return toArray(new Object[size()]); 737 } 738 739 public Object[] toArray(Object[] trg) { 740 System.arraycopy(data, 0, trg, 0, data.length>trg.length?trg.length:data.length); 741 return trg; 742 } 743 744 @Override 745 public QueryColumnPro toDebugColumn() { 746 return _toDebugColumn(); 747 } 748 749 public DebugQueryColumn _toDebugColumn() { 750 return new DebugQueryColumn(data,key,query,size,type,typeChecked); 751 } 752 753 @Override 754 public java.util.Iterator<String> getIterator() { 755 return keysAsStringIterator(); 756 } 757 758 @Override 759 public boolean equals(Object obj){ 760 if(!(obj instanceof Collection)) return false; 761 return CollectionUtil.equals(this,(Collection)obj); 762 } 763 764 @Override 765 public int hashCode() { 766 return CollectionUtil.hashCode(this); 767 } 768 }