001 package railo.runtime.type; 002 003 import java.util.ArrayList; 004 import java.util.Arrays; 005 import java.util.Comparator; 006 import java.util.Iterator; 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.DumpTable; 015 import railo.runtime.dump.DumpUtil; 016 import railo.runtime.dump.SimpleDumpData; 017 import railo.runtime.exp.ExpressionException; 018 import railo.runtime.exp.PageException; 019 import railo.runtime.op.Caster; 020 import railo.runtime.op.Duplicator; 021 import railo.runtime.op.ThreadLocalDuplication; 022 import railo.runtime.type.it.EntryIterator; 023 import railo.runtime.type.it.KeyIterator; 024 import railo.runtime.type.it.StringIterator; 025 import railo.runtime.type.util.ArraySupport; 026 import railo.runtime.type.util.ListIteratorImpl; 027 028 029 030 /** 031 * CFML array object 032 */ 033 public class ArrayImpl extends ArraySupport implements Sizeable { 034 035 private static final long serialVersionUID = -6187994169003839005L; 036 037 private Object[] arr; 038 private int dimension=1; 039 private final int cap=32; 040 private int size=0; 041 private int offset=0; 042 private int offCount=0; 043 044 /** 045 * constructor with definiton of the dimension 046 * @param dimension dimension goes from one to 3 047 * @throws ExpressionException 048 */ 049 public ArrayImpl(int dimension) throws ExpressionException { 050 if(dimension>3 || dimension<1) 051 throw new ExpressionException("Array Dimension must be between 1 and 3"); 052 this.dimension=dimension; 053 arr=new Object[offset+cap]; 054 } 055 056 /** 057 * constructor with default dimesnion (1) 058 */ 059 public ArrayImpl() { 060 arr=new Object[offset+cap]; 061 } 062 063 /** 064 * constructor with to data to fill 065 * @param objects Objects array data to fill 066 */ 067 public ArrayImpl(Object[] objects) { 068 arr=new Object[objects.length]; 069 for(int i=0;i<arr.length;i++){ 070 arr[i]=objects[i]; 071 } 072 size=arr.length; 073 offset=0; 074 } 075 076 /** 077 * return dimension of the array 078 * @return dimension of the array 079 */ 080 public int getDimension() { 081 return dimension; 082 } 083 084 @Override 085 public Object get(String key) throws ExpressionException { 086 return getE(Caster.toIntValue(key)); 087 } 088 089 @Override 090 public Object get(Collection.Key key) throws ExpressionException { 091 return getE(Caster.toIntValue(key.getString())); 092 } 093 094 @Override 095 public Object get(String key, Object defaultValue) { 096 double index=Caster.toIntValue(key,Integer.MIN_VALUE); 097 if(index==Integer.MIN_VALUE) return defaultValue; 098 return get((int)index,defaultValue); 099 } 100 101 @Override 102 public Object get(Collection.Key key, Object defaultValue) { 103 double index=Caster.toIntValue(key.getString(),Integer.MIN_VALUE); 104 if(index==Integer.MIN_VALUE) return defaultValue; 105 return get((int)index,defaultValue); 106 } 107 108 @Override 109 public synchronized Object get(int key, Object defaultValue) { 110 if(key>size || key<1) { 111 if(dimension>1) { 112 ArrayImpl ai = new ArrayImpl(); 113 ai.dimension=dimension-1; 114 return setEL(key,ai); 115 } 116 return defaultValue; 117 } 118 Object o=arr[(offset+key)-1]; 119 120 if(o==null) { 121 if(dimension>1) { 122 ArrayImpl ai = new ArrayImpl(); 123 ai.dimension=dimension-1; 124 return setEL(key,ai); 125 } 126 if(!NullSupportHelper.full()) return defaultValue; 127 } 128 return o; 129 } 130 131 @Override 132 public synchronized Object getE(int key) throws ExpressionException { 133 if(key<1) { 134 throw invalidPosition(key); 135 } 136 else if(key>size) { 137 if(dimension>1)return setE(key,new ArrayImpl(dimension-1)); 138 throw invalidPosition(key); 139 } 140 141 Object o=arr[(offset+key)-1]; 142 143 if(NullSupportHelper.full()) { 144 if(o==null && dimension>1) return setE(key,new ArrayImpl(dimension-1)); 145 return o; 146 } 147 148 if(o==null) { 149 if(dimension>1) return setE(key,new ArrayImpl(dimension-1)); 150 throw invalidPosition(key); 151 } 152 return o; 153 } 154 155 /** 156 * Exception method if key doesn't exist at given position 157 * @param pos 158 * @return exception 159 */ 160 private ExpressionException invalidPosition(int pos) { 161 return new ExpressionException("Element at position ["+pos+"] doesn't exist in array"); 162 } 163 164 @Override 165 public Object setEL(String key, Object value) { 166 try { 167 return setEL(Caster.toIntValue(key), value); 168 } catch (ExpressionException e) { 169 return null; 170 } 171 } 172 173 @Override 174 public Object setEL(Collection.Key key, Object value) { 175 try { 176 return setEL(Caster.toIntValue(key.getString()), value); 177 } catch (ExpressionException e) { 178 return null; 179 } 180 } 181 182 @Override 183 public Object set(String key, Object value) throws ExpressionException { 184 return setE(Caster.toIntValue(key),value); 185 } 186 187 @Override 188 public Object set(Collection.Key key, Object value) throws ExpressionException { 189 return setE(Caster.toIntValue(key.getString()),value); 190 } 191 192 @Override 193 public synchronized Object setEL(int key, Object value) { 194 if(offset+key>arr.length)enlargeCapacity(key); 195 if(key>size)size=key; 196 arr[(offset+key)-1]=checkValueEL(value); 197 return value; 198 } 199 200 /** 201 * set value at defined position 202 * @param key 203 * @param value 204 * @return defined value 205 * @throws ExpressionException 206 */ 207 public synchronized Object setE(int key, Object value) throws ExpressionException { 208 if(key<1)throw new ExpressionException("Invalid index ["+key+"] for array. Index must be a positive integer (1, 2, 3, ...)"); 209 if(offset+key>arr.length)enlargeCapacity(key); 210 if(key>size)size=key; 211 arr[(offset+key)-1]=checkValue(value); 212 return value; 213 } 214 215 /** 216 * !!! all methods that use this method must be sync 217 * enlarge the inner array to given size 218 * @param key min size of the array 219 */ 220 private void enlargeCapacity(int key) { 221 int diff=offCount-offset; 222 int newSize=arr.length; 223 if(newSize<1) newSize=1; 224 while(newSize<key+offset+diff) { 225 newSize*=2; 226 } 227 if(newSize>arr.length) { 228 Object[] na=new Object[newSize]; 229 for(int i=offset;i<offset+size;i++) { 230 na[i+diff]=arr[i]; 231 } 232 arr=na; 233 offset+=diff; 234 } 235 } 236 237 /** 238 * !!! all methods that use this method must be sync 239 * enlarge the offset if 0 240 */ 241 private void enlargeOffset() { 242 if(offset==0) { 243 offCount=offCount==0?1:offCount*2; 244 offset=offCount; 245 Object[] narr=new Object[arr.length+offset]; 246 for(int i=0;i<size;i++) { 247 narr[offset+i]=arr[i]; 248 } 249 arr=narr; 250 } 251 } 252 253 /** 254 * !!! all methods that use this method must be sync 255 * check if value is valid to insert to array (to a multidimesnional array only array with one smaller dimension can be inserted) 256 * @param value value to check 257 * @return checked value 258 * @throws ExpressionException 259 */ 260 private Object checkValue(Object value) throws ExpressionException { 261 // is a 1 > Array 262 if(dimension>1) { 263 if(value instanceof Array) { 264 if(((Array)value).getDimension()!=dimension-1) 265 throw new ExpressionException("You can only Append an Array with "+(dimension-1)+" Dimension","array has wrong dimension, now is "+(((Array)value).getDimension())+ " but it must be "+(dimension-1)); 266 } 267 else 268 throw new ExpressionException("You can only Append an Array with "+(dimension-1)+" Dimension","now is a object of type "+Caster.toClassName(value)); 269 } 270 return value; 271 } 272 273 /** 274 * !!! all methods that use this method must be sync 275 * check if value is valid to insert to array (to a multidimesnional array only array with one smaller dimension can be inserted), if value is invalid return null; 276 * @param value value to check 277 * @return checked value 278 */ 279 private Object checkValueEL(Object value) { 280 // is a 1 > Array 281 if(dimension>1) { 282 if(value instanceof Array) { 283 if(((Array)value).getDimension()!=dimension-1) 284 return null; 285 } 286 else 287 return null; 288 } 289 return value; 290 } 291 292 @Override 293 public int size() { 294 return size; 295 } 296 297 @Override 298 public synchronized Collection.Key[] keys() { 299 300 ArrayList<Collection.Key> lst=new ArrayList<Collection.Key>(); 301 int count=0; 302 for(int i=offset;i<offset+size;i++) { 303 Object o=arr[i]; 304 count++; 305 if(o!=null) lst.add(KeyImpl.getInstance(count+"")); 306 } 307 return lst.toArray(new Collection.Key[lst.size()]); 308 } 309 310 @Override 311 public synchronized int[] intKeys() { 312 ArrayList<Integer> lst=new ArrayList<Integer>(); 313 int count=0; 314 for(int i=offset;i<offset+size;i++) { 315 Object o=arr[i]; 316 count++; 317 if(o!=null) lst.add(Integer.valueOf(count)); 318 } 319 320 int[] ints=new int[lst.size()]; 321 322 for(int i=0;i<ints.length;i++){ 323 ints[i]=lst.get(i).intValue(); 324 } 325 return ints; 326 } 327 328 @Override 329 public Object remove(Collection.Key key) throws ExpressionException { 330 return removeE(Caster.toIntValue(key.getString())); 331 } 332 333 public Object removeEL(Collection.Key key) { 334 return removeEL(Caster.toIntValue(key.getString(),-1)); 335 } 336 337 @Override 338 public synchronized Object removeE(int key) throws ExpressionException { 339 if(key>size || key<1) throw invalidPosition(key); 340 Object obj=get(key,null); 341 for(int i=(offset+key)-1;i<(offset+size)-1;i++) { 342 arr[i]=arr[i+1]; 343 } 344 size--; 345 return obj; 346 } 347 348 @Override 349 public synchronized Object removeEL(int key) { 350 if(key>size || key<1) return null; 351 Object obj=get(key,null); 352 353 for(int i=(offset+key)-1;i<(offset+size)-1;i++) { 354 arr[i]=arr[i+1]; 355 } 356 size--; 357 return obj; 358 } 359 360 @Override 361 public synchronized void clear() { 362 if(size()>0) { 363 arr=new Object[cap]; 364 size=0; 365 offCount=1; 366 offset=0; 367 } 368 } 369 370 @Override 371 public synchronized boolean insert(int key, Object value) throws ExpressionException { 372 if(key<1 || key>size+1) { 373 throw new ExpressionException("can't insert value to array at position "+key+", array goes from 1 to "+size()); 374 } 375 // Left 376 if((size/2)>=key) { 377 enlargeOffset(); 378 for(int i=offset;i<(offset+key)-1;i++) { 379 arr[i-1]=arr[i]; 380 } 381 offset--; 382 arr[(offset+key)-1]=checkValue(value); 383 size++; 384 } 385 // Right 386 else { 387 if((offset+key)>arr.length || size+offset>=arr.length)enlargeCapacity(arr.length+2); 388 for(int i=size+offset;i>=key+offset;i--) { 389 arr[i]=arr[i-1]; 390 } 391 arr[(offset+key)-1]=checkValue(value); 392 size++; 393 394 } 395 return true; 396 } 397 398 @Override 399 public synchronized Object append(Object o) throws ExpressionException { 400 if(offset+size+1>arr.length)enlargeCapacity(size+1); 401 arr[offset+size]=checkValue(o); 402 size++; 403 return o; 404 } 405 406 /** 407 * append a new value to the end of the array 408 * @param o value to insert 409 * @return inserted value 410 */ 411 public synchronized Object appendEL(Object o) { 412 413 if(offset+size+1>arr.length)enlargeCapacity(size+1); 414 arr[offset+size]=o; 415 size++; 416 return o; 417 } 418 419 /** 420 * append a new value to the end of the array 421 * @param str value to insert 422 * @return inserted value 423 */ 424 public synchronized String _append(String str) { 425 if(offset+size+1>arr.length)enlargeCapacity(size+1); 426 arr[offset+size]=str; 427 size++; 428 return str; 429 } 430 431 /** 432 * add a new value to the begin of the array 433 * @param o value to insert 434 * @return inserted value 435 * @throws ExpressionException 436 */ 437 public Object prepend(Object o) throws ExpressionException { 438 insert(1,o); 439 return o; 440 } 441 442 /** 443 * resize array to defined size 444 * @param to new minimum size of the array 445 */ 446 public synchronized void resize(int to) { 447 if(to>size) { 448 enlargeCapacity(to); 449 size=to; 450 } 451 } 452 453 public synchronized void sort(Comparator comp) throws PageException { 454 if(getDimension()>1) 455 throw new ExpressionException("only 1 dimensional arrays can be sorted"); 456 Arrays.sort(arr,offset,offset+size,comp); 457 } 458 459 /** 460 * @return return arra as native (Java) Object Array 461 */ 462 public synchronized Object[] toArray() { 463 Object[] rtn=new Object[size]; 464 int count=0; 465 for(int i=offset;i<offset+size;i++) { 466 rtn[count++]=arr[i]; 467 } 468 469 return rtn; 470 } 471 472 /** 473 * @return return array as ArrayList 474 */ 475 /*public synchronized ArrayList toArrayList() { 476 ArrayList al=new ArrayList(); 477 for(int i=offset;i<offset+size;i++) { 478 al.add(arr[i]); 479 } 480 return al; 481 }*/ 482 483 @Override 484 public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { 485 DumpTable table = new DumpTable("array","#99cc33","#ccff33","#000000"); 486 table.setTitle("Array"); 487 //if(size()>10)table.setComment("Size:"+size()); 488 int length=size(); 489 maxlevel--; 490 for(int i=1;i<=length;i++) { 491 Object o=null; 492 try { 493 o = getE(i); 494 } 495 catch (Exception e) {} 496 table.appendRow(1,new SimpleDumpData(i),DumpUtil.toDumpData(o, pageContext,maxlevel,dp)); 497 } 498 return table; 499 } 500 501 /** 502 * return code print of the array as plain text 503 * @return content as string 504 */ 505 public synchronized String toPlain() { 506 507 StringBuffer sb=new StringBuffer(); 508 int length=size(); 509 for(int i=1;i<=length;i++) { 510 sb.append(i); 511 sb.append(": "); 512 sb.append(get(i-1,null)); 513 sb.append("\n"); 514 } 515 return sb.toString(); 516 } 517 518 @Override 519 public synchronized Collection duplicate(boolean deepCopy) { 520 return duplicate(new ArrayImpl(),deepCopy); 521 } 522 523 524 525 protected Collection duplicate(ArrayImpl arr,boolean deepCopy) { 526 arr.dimension=dimension; 527 Collection.Key[] keys=this.keys(); 528 ThreadLocalDuplication.set(this, arr); 529 Collection.Key k; 530 try { 531 for(int i=0;i<keys.length;i++) { 532 k=keys[i]; 533 if(deepCopy)arr.set(k,Duplicator.duplicate(this.get(k,null),deepCopy)); 534 else arr.set(k,this.get(k,null)); 535 } 536 } 537 catch (ExpressionException e) {} 538 finally{ 539 // ThreadLocalDuplication.remove(this); removed "remove" to catch sisters and brothers 540 } 541 542 return arr; 543 } 544 545 @Override 546 public Iterator<Collection.Key> keyIterator() { 547 return new KeyIterator(keys()); 548 } 549 550 @Override 551 public Iterator<String> keysAsStringIterator() { 552 return new StringIterator(keys()); 553 } 554 555 @Override 556 public Iterator<Entry<Key, Object>> entryIterator() { 557 return new EntryIterator(this, keys()); 558 } 559 560 561 @Override 562 public Iterator iterator() { 563 return new ListIteratorImpl(this,0); 564 } 565 566 @Override 567 public long sizeOf() { 568 return SizeOf.size(arr) 569 +SizeOf.size(dimension) 570 +SizeOf.size(cap) 571 +SizeOf.size(size) 572 +SizeOf.size(offset) 573 +SizeOf.size(offCount) 574 +SizeOf.REF_SIZE; 575 } 576 }