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.type; 020 021import java.io.Externalizable; 022import java.io.IOException; 023import java.io.ObjectInput; 024import java.io.ObjectOutput; 025import java.util.Date; 026 027import lucee.commons.digest.WangJenkins; 028import lucee.commons.lang.SizeOf; 029import lucee.commons.lang.StringUtil; 030import lucee.runtime.exp.CasterException; 031import lucee.runtime.exp.PageException; 032import lucee.runtime.op.Castable; 033import lucee.runtime.op.Caster; 034import lucee.runtime.op.Operator; 035import lucee.runtime.op.date.DateCaster; 036import lucee.runtime.type.Collection.Key; 037import lucee.runtime.type.dt.DateTime; 038 039public class KeyImpl implements Collection.Key,Castable,Comparable,Sizeable,Externalizable,WangJenkins { 040 041 private static final long serialVersionUID = -8864844181140115609L; // do not change 042 043 044 private static final long[] byteTable = createLookupTable(); 045 private static final long HSTART = 0xBB40E64DA205B064L; 046 private static final long HMULT = 7664345821815920749L; 047 048 //private boolean intern; 049 private String key; 050 private transient String lcKey; 051 private transient String ucKey; 052 private transient int wjh; 053 private transient int sfm=-1; 054 private transient long h64; 055 056 public KeyImpl() { 057 // DO NOT USE, JUST FOR UNSERIALIZE 058 } 059 060 private static final long[] createLookupTable() { 061 long[] _byteTable = new long[256]; 062 long h = 0x544B2FBACAAF1684L; 063 for (int i = 0; i < 256; i++) { 064 for (int j = 0; j < 31; j++) { 065 h = (h >>> 7) ^ h; 066 h = (h << 11) ^ h; 067 h = (h >>> 10) ^ h; 068 } 069 _byteTable[i] = h; 070 } 071 return _byteTable; 072 } 073 074 private static final long createHash64(CharSequence cs) { 075 long h = HSTART; 076 final long hmult = HMULT; 077 final long[] ht = byteTable; 078 final int len = cs.length(); 079 for (int i = 0; i < len; i++) { 080 char ch = cs.charAt(i); 081 h = (h * hmult) ^ ht[ch & 0xff]; 082 h = (h * hmult) ^ ht[(ch >>> 8) & 0xff]; 083 } 084 return h; 085 } 086 087 @Override 088 public int wangJenkinsHash() { 089 if(wjh==0) { 090 int h = hashCode(); 091 h += (h << 15) ^ 0xffffcd7d; 092 h ^= (h >>> 10); 093 h += (h << 3); 094 h ^= (h >>> 6); 095 h += (h << 2) + (h << 14); 096 wjh= h ^ (h >>> 16); 097 } 098 return wjh; 099 } 100 101 public int slotForMap() { 102 if(sfm == -1) { 103 int h = 0; 104 h ^= hashCode(); 105 h ^= (h >>> 20) ^ (h >>> 12); 106 sfm = h ^ (h >>> 7) ^ (h >>> 4); 107 } 108 return sfm; 109 } 110 111 public void writeExternal(ObjectOutput out) throws IOException { 112 out.writeObject(key); 113 } 114 115 public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException { 116 key=(String) in.readObject(); 117 ucKey=key.toUpperCase(); 118 h64=createHash64(ucKey); 119 } 120 121 public KeyImpl(String key) { 122 this.key=key; 123 this.ucKey=key.toUpperCase(); 124 h64=createHash64(ucKey); 125 } 126 127 /** 128 * for dynamic loading of key objects 129 * @param string 130 * @return 131 */ 132 public static Collection.Key init(String key) { 133 return new KeyImpl(key); 134 } 135 136 public static Collection.Key _const(String key) { 137 return new KeyImpl(key); 138 } 139 140 public static Collection.Key getInstance(String key) { 141 return new KeyImpl(key); 142 } 143 144 public static Collection.Key intern(String key) { 145 return new KeyImpl(key); 146 } 147 148 @Override 149 public char charAt(int index) { 150 return key.charAt(index); 151 } 152 153 @Override 154 public char lowerCharAt(int index) { 155 return getLowerString().charAt(index); 156 } 157 158 public char upperCharAt(int index) { 159 return ucKey.charAt(index); 160 } 161 162 @Override 163 public String getLowerString() { 164 if(lcKey==null)lcKey=StringUtil.toLowerCase(key); 165 return lcKey; 166 } 167 168 public String getUpperString() { 169 return ucKey; 170 } 171 172 @Override 173 public String toString() { 174 return key; 175 } 176 177 @Override 178 public String getString() { 179 return key; 180 } 181 182 @Override 183 public boolean equals(Object other) { 184 if(this==other) return true; 185 if(other instanceof KeyImpl) { 186 return hash()==((KeyImpl)other).hash(); 187 } 188 if(other instanceof String) { 189 return key.equalsIgnoreCase((String)other); 190 } 191 if(other instanceof Key) { 192 return ucKey.equalsIgnoreCase(((Key)other).getUpperString()); 193 } 194 return false; 195 } 196 197 198 @Override 199 public boolean equalsIgnoreCase(Key other) { 200 if(this==other) return true; 201 if(other instanceof KeyImpl) { 202 return h64==((KeyImpl)other).h64;//return lcKey.equals((((KeyImpl)other).lcKey)); 203 } 204 return ucKey.equalsIgnoreCase(other.getLowerString()); 205 } 206 207 @Override 208 public int hashCode() { 209 return ucKey.hashCode(); 210 } 211 212 // FUTURE add to interface 213 public long hash() { 214 return h64; 215 } 216 217 @Override 218 public int getId() {// set to deprecated, use instead hash() 219 return hashCode(); 220 } 221 222 @Override 223 public boolean castToBooleanValue() throws PageException { 224 return Caster.toBooleanValue(key); 225 } 226 227 @Override 228 public Boolean castToBoolean(Boolean defaultValue) { 229 return Caster.toBoolean(key,defaultValue); 230 } 231 232 @Override 233 public DateTime castToDateTime() throws PageException { 234 return Caster.toDatetime(key,null); 235 } 236 237 @Override 238 public DateTime castToDateTime(DateTime defaultValue) { 239 return DateCaster.toDateAdvanced(key,DateCaster.CONVERTING_TYPE_OFFSET,null,defaultValue); 240 } 241 242 @Override 243 public double castToDoubleValue() throws PageException { 244 return Caster.toDoubleValue(key); 245 } 246 247 @Override 248 public double castToDoubleValue(double defaultValue) { 249 return Caster.toDoubleValue(key,defaultValue); 250 } 251 252 @Override 253 public String castToString() throws PageException { 254 return key; 255 } 256 257 @Override 258 public String castToString(String defaultValue) { 259 return key; 260 } 261 262 @Override 263 public int compareTo(boolean b) throws PageException { 264 return Operator.compare(key, b); 265 } 266 267 @Override 268 public int compareTo(DateTime dt) throws PageException { 269 return Operator.compare(key, (Date)dt); 270 } 271 272 @Override 273 public int compareTo(double d) throws PageException { 274 return Operator.compare(key, d); 275 } 276 277 @Override 278 public int compareTo(String str) throws PageException { 279 return Operator.compare(key, str); 280 } 281 282 283 public int compareTo(Object o) { 284 try { 285 return Operator.compare(key, o); 286 } catch (PageException e) { 287 ClassCastException cce = new ClassCastException(e.getMessage()); 288 cce.setStackTrace(e.getStackTrace()); 289 throw cce; 290 291 } 292 } 293 294 295 public static Array toUpperCaseArray(Key[] keys) { 296 ArrayImpl arr=new ArrayImpl(); 297 for(int i=0;i<keys.length;i++) { 298 arr._append(((KeyImpl)keys[i]).getUpperString()); 299 } 300 return arr; 301 } 302 public static Array toLowerCaseArray(Key[] keys) { 303 ArrayImpl arr=new ArrayImpl(); 304 for(int i=0;i<keys.length;i++) { 305 arr._append(((KeyImpl)keys[i]).getLowerString()); 306 } 307 return arr; 308 } 309 310 public static Array toArray(Key[] keys) { 311 ArrayImpl arr=new ArrayImpl(); 312 for(int i=0;i<keys.length;i++) { 313 arr._append(((KeyImpl)keys[i]).getString()); 314 } 315 return arr; 316 } 317 318 public static String toUpperCaseList(Key[] array, String delimiter) { 319 if(array.length==0) return ""; 320 StringBuffer sb=new StringBuffer(((KeyImpl)array[0]).getUpperString()); 321 322 if(delimiter.length()==1) { 323 char c=delimiter.charAt(0); 324 for(int i=1;i<array.length;i++) { 325 sb.append(c); 326 sb.append(((KeyImpl)array[i]).getUpperString()); 327 } 328 } 329 else { 330 for(int i=1;i<array.length;i++) { 331 sb.append(delimiter); 332 sb.append(((KeyImpl)array[i]).getUpperString()); 333 } 334 } 335 return sb.toString(); 336 } 337 338 public static String toList(Key[] array, String delimiter) { 339 if(array.length==0) return ""; 340 StringBuilder sb=new StringBuilder(((KeyImpl)array[0]).getString()); 341 342 if(delimiter.length()==1) { 343 char c=delimiter.charAt(0); 344 for(int i=1;i<array.length;i++) { 345 sb.append(c); 346 sb.append((array[i]).getString()); 347 } 348 } 349 else { 350 for(int i=1;i<array.length;i++) { 351 sb.append(delimiter); 352 sb.append((array[i]).getString()); 353 } 354 } 355 return sb.toString(); 356 } 357 358 public static String toLowerCaseList(Key[] array, String delimiter) { 359 if(array.length==0) return ""; 360 StringBuffer sb=new StringBuffer(((KeyImpl)array[0]).getLowerString()); 361 362 if(delimiter.length()==1) { 363 char c=delimiter.charAt(0); 364 for(int i=1;i<array.length;i++) { 365 sb.append(c); 366 sb.append(((KeyImpl)array[i]).getLowerString()); 367 } 368 } 369 else { 370 for(int i=1;i<array.length;i++) { 371 sb.append(delimiter); 372 sb.append(((KeyImpl)array[i]).getLowerString()); 373 } 374 } 375 return sb.toString(); 376 } 377 378 public static Collection.Key toKey(Object obj, Collection.Key defaultValue) { 379 if(obj instanceof Collection.Key) return (Collection.Key) obj; 380 String str = Caster.toString(obj,null); 381 if(str==null) return defaultValue; 382 return init(str); 383 } 384 385 public static Collection.Key toKey(Object obj) throws CasterException { 386 if(obj instanceof Collection.Key) return (Collection.Key) obj; 387 String str = Caster.toString(obj,null); 388 if(str==null) throw new CasterException(obj,Collection.Key.class); 389 return init(str); 390 } 391 392 393 public long sizeOf() { 394 return 395 SizeOf.size(this.key)+ 396 SizeOf.size(this.lcKey)+ 397 SizeOf.size(this.ucKey)+ 398 SizeOf.REF_SIZE; 399 } 400 401 @Override 402 public int length() { 403 return key.length(); 404 } 405 406 407 public static Key[] toKeyArray(String[] arr) { 408 if(arr==null) return null; 409 410 Key[] keys=new Key[arr.length]; 411 for(int i=0;i<keys.length;i++){ 412 keys[i]=init(arr[i]); 413 } 414 return keys; 415 } 416}