001 /* 002 * Copyright 2009 Red Hat, Inc. 003 * Red Hat licenses this file to you under the Apache License, version 004 * 2.0 (the "License"); you may not use this file except in compliance 005 * with the License. You may obtain a copy of the License at 006 * http://www.apache.org/licenses/LICENSE-2.0 007 * Unless required by applicable law or agreed to in writing, software 008 * distributed under the License is distributed on an "AS IS" BASIS, 009 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 010 * implied. See the License for the specific language governing 011 * permissions and limitations under the License. 012 */ 013 014 package org.hornetq.api.core; 015 016 import java.io.Serializable; 017 import java.util.ArrayList; 018 import java.util.List; 019 020 import org.hornetq.core.logging.Logger; 021 import org.hornetq.utils.DataConstants; 022 023 /** 024 * A simple String class that can store all characters, and stores as simple byte[], 025 * this minimises expensive copying between String objects. 026 * 027 * This object is used heavily throughout HornetQ for performance reasons. 028 * 029 * @author <a href="mailto:tim.fox@jboss.com">Tim Fox</a> 030 * 031 */ 032 033 public class SimpleString implements CharSequence, Serializable, Comparable<SimpleString> 034 { 035 private static final long serialVersionUID = 4204223851422244307L; 036 037 private static final Logger log = Logger.getLogger(SimpleString.class); 038 039 // Attributes 040 // ------------------------------------------------------------------------ 041 private final byte[] data; 042 043 private transient int hash; 044 045 // Cache the string 046 private transient String str; 047 048 // Static 049 // ---------------------------------------------------------------------- 050 051 /** 052 * Returns a SimpleString constructed from the <code>string</code> parameter. 053 * If <code>string</code> is <code>null</code>, the return value will be <code>null</code> too. 054 */ 055 public static SimpleString toSimpleString(final String string) 056 { 057 if (string == null) 058 { 059 return null; 060 } 061 return new SimpleString(string); 062 } 063 064 // Constructors 065 // ---------------------------------------------------------------------- 066 /** 067 * creates a SimpleString from a conventional String 068 * @param string the string to transform 069 */ 070 public SimpleString(final String string) 071 { 072 int len = string.length(); 073 074 data = new byte[len << 1]; 075 076 int j = 0; 077 078 for (int i = 0; i < len; i++) 079 { 080 char c = string.charAt(i); 081 082 byte low = (byte)(c & 0xFF); // low byte 083 084 data[j++] = low; 085 086 byte high = (byte)(c >> 8 & 0xFF); // high byte 087 088 data[j++] = high; 089 } 090 091 str = string; 092 } 093 094 /** 095 * creates a SimpleString from a byte array 096 * @param data the byte array to use 097 */ 098 public SimpleString(final byte[] data) 099 { 100 this.data = data; 101 } 102 103 // CharSequence implementation 104 // --------------------------------------------------------------------------- 105 106 public int length() 107 { 108 return data.length >> 1; 109 } 110 111 public char charAt(int pos) 112 { 113 if (pos < 0 || pos >= data.length >> 1) 114 { 115 throw new IndexOutOfBoundsException(); 116 } 117 pos <<= 1; 118 119 return (char)(data[pos] | data[pos + 1] << 8); 120 } 121 122 public CharSequence subSequence(final int start, final int end) 123 { 124 int len = data.length >> 1; 125 126 if (end < start || start < 0 || end > len) 127 { 128 throw new IndexOutOfBoundsException(); 129 } 130 else 131 { 132 int newlen = end - start << 1; 133 byte[] bytes = new byte[newlen]; 134 135 System.arraycopy(data, start << 1, bytes, 0, newlen); 136 137 return new SimpleString(bytes); 138 } 139 } 140 141 // Comparable implementation ------------------------------------- 142 143 public int compareTo(final SimpleString o) 144 { 145 return toString().compareTo(o.toString()); 146 } 147 148 // Public 149 // --------------------------------------------------------------------------- 150 151 /** 152 * returns the underlying byte array of this SimpleString 153 * @return the byte array 154 */ 155 public byte[] getData() 156 { 157 return data; 158 } 159 160 /** 161 * returns true if the SimpleString parameter starts with the same data as this one. false if not. 162 * @param other the SimpelString to look for 163 * @return true if this SimpleString starts with the same data 164 */ 165 public boolean startsWith(final SimpleString other) 166 { 167 byte[] otherdata = other.data; 168 169 if (otherdata.length > data.length) 170 { 171 return false; 172 } 173 174 for (int i = 0; i < otherdata.length; i++) 175 { 176 if (data[i] != otherdata[i]) 177 { 178 return false; 179 } 180 } 181 182 return true; 183 } 184 185 @Override 186 public String toString() 187 { 188 if (str == null) 189 { 190 int len = data.length >> 1; 191 192 char[] chars = new char[len]; 193 194 int j = 0; 195 196 for (int i = 0; i < len; i++) 197 { 198 int low = data[j++] & 0xFF; 199 200 int high = data[j++] << 8 & 0xFF00; 201 202 chars[i] = (char)(low | high); 203 } 204 205 str = new String(chars); 206 } 207 208 return str; 209 } 210 211 @Override 212 public boolean equals(final Object other) 213 { 214 if (this == other) 215 { 216 return true; 217 } 218 219 if (other instanceof SimpleString) 220 { 221 SimpleString s = (SimpleString)other; 222 223 if (data.length != s.data.length) 224 { 225 return false; 226 } 227 228 for (int i = 0; i < data.length; i++) 229 { 230 if (data[i] != s.data[i]) 231 { 232 return false; 233 } 234 } 235 236 return true; 237 } 238 else 239 { 240 return false; 241 } 242 } 243 244 @Override 245 public int hashCode() 246 { 247 if (hash == 0) 248 { 249 int tmphash = 0; 250 for (byte element : data) 251 { 252 tmphash = (tmphash << 5) - tmphash + element; // (hash << 5) - hash is same as hash * 31 253 } 254 hash = tmphash; 255 } 256 257 return hash; 258 } 259 260 /** 261 * splits this SimpleString into an array of SimpleString using the char param as the delimeter. 262 * 263 * i.e. "a.b" would return "a" and "b" if . was the delimeter 264 * @param delim 265 */ 266 public SimpleString[] split(final char delim) 267 { 268 if (!contains(delim)) 269 { 270 return new SimpleString[] { this }; 271 } 272 else 273 { 274 List<SimpleString> all = new ArrayList<SimpleString>(); 275 int lasPos = 0; 276 for (int i = 0; i < data.length; i += 2) 277 { 278 byte low = (byte)(delim & 0xFF); // low byte 279 byte high = (byte)(delim >> 8 & 0xFF); // high byte 280 if (data[i] == low && data[i + 1] == high) 281 { 282 byte[] bytes = new byte[i - lasPos]; 283 System.arraycopy(data, lasPos, bytes, 0, bytes.length); 284 lasPos = i + 2; 285 all.add(new SimpleString(bytes)); 286 } 287 } 288 byte[] bytes = new byte[data.length - lasPos]; 289 System.arraycopy(data, lasPos, bytes, 0, bytes.length); 290 all.add(new SimpleString(bytes)); 291 SimpleString[] parts = new SimpleString[all.size()]; 292 return all.toArray(parts); 293 } 294 } 295 296 /** 297 * checks to see if this SimpleString contains the char parameter passed in 298 * 299 * @param c the char to check for 300 * @return true if the char is found, false otherwise. 301 */ 302 public boolean contains(final char c) 303 { 304 for (int i = 0; i < data.length; i += 2) 305 { 306 byte low = (byte)(c & 0xFF); // low byte 307 byte high = (byte)(c >> 8 & 0xFF); // high byte 308 if (data[i] == low && data[i + 1] == high) 309 { 310 return true; 311 } 312 } 313 return false; 314 } 315 316 /** 317 * concatanates a SimpleString and a String 318 * 319 * @param toAdd the String to concate with. 320 * @return the concatanated SimpleString 321 */ 322 public SimpleString concat(final String toAdd) 323 { 324 return concat(new SimpleString(toAdd)); 325 } 326 327 /** 328 * concatanates 2 SimpleString's 329 * 330 * @param toAdd the SimpleString to concate with. 331 * @return the concatanated SimpleString 332 */ 333 public SimpleString concat(final SimpleString toAdd) 334 { 335 byte[] bytes = new byte[data.length + toAdd.getData().length]; 336 System.arraycopy(data, 0, bytes, 0, data.length); 337 System.arraycopy(toAdd.getData(), 0, bytes, data.length, toAdd.getData().length); 338 return new SimpleString(bytes); 339 } 340 341 /** 342 * concatanates a SimpleString and a char 343 * 344 * @param c the char to concate with. 345 * @return the concatanated SimpleString 346 */ 347 public SimpleString concat(final char c) 348 { 349 byte[] bytes = new byte[data.length + 2]; 350 System.arraycopy(data, 0, bytes, 0, data.length); 351 bytes[data.length] = (byte)(c & 0xFF); 352 bytes[data.length + 1] = (byte)(c >> 8 & 0xFF); 353 return new SimpleString(bytes); 354 } 355 356 /** 357 * returns the size of this SimpleString 358 * @return the size 359 */ 360 public int sizeof() 361 { 362 return DataConstants.SIZE_INT + data.length; 363 } 364 365 /** 366 * returns the size of a SimpleString 367 * @param str the SimpleString to check 368 * @return the size 369 */ 370 public static int sizeofString(final SimpleString str) 371 { 372 return str.sizeof(); 373 } 374 375 /** 376 * returns the size of a SimpleString which could be null 377 * @param str the SimpleString to check 378 * @return the size 379 */ 380 public static int sizeofNullableString(final SimpleString str) 381 { 382 if (str == null) 383 { 384 return 1; 385 } 386 else 387 { 388 return 1 + str.sizeof(); 389 } 390 } 391 392 /** 393 * 394 * @param srcBegin 395 * @param srcEnd 396 * @param dst 397 * @param dstBegin 398 */ 399 public void getChars(final int srcBegin, final int srcEnd, final char dst[], final int dstBegin) 400 { 401 if (srcBegin < 0) 402 { 403 throw new StringIndexOutOfBoundsException(srcBegin); 404 } 405 if (srcEnd > length()) 406 { 407 throw new StringIndexOutOfBoundsException(srcEnd); 408 } 409 if (srcBegin > srcEnd) 410 { 411 throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); 412 } 413 414 int j = 0; 415 416 for (int i = srcBegin; i < srcEnd - srcBegin; i++) 417 { 418 int low = data[j++] & 0xFF; 419 420 int high = data[j++] << 8 & 0xFF00; 421 422 dst[i] = (char)(low | high); 423 } 424 } 425 426 }