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