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 014package org.hornetq.api.core; 015 016import java.io.Serializable; 017import java.util.ArrayList; 018import java.util.List; 019 020import org.hornetq.core.logging.Logger; 021import 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 033public 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] & 0xFF) | (data[pos + 1] << 8) & 0xFF00); 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 delimiter. 262 * i.e. "a.b" would return "a" and "b" if . was the delimiter 263 * @param delim 264 */ 265 public SimpleString[] split(final char delim) 266 { 267 if (!contains(delim)) 268 { 269 return new SimpleString[] { this }; 270 } 271 else 272 { 273 List<SimpleString> all = new ArrayList<SimpleString>(); 274 275 byte low = (byte)(delim & 0xFF); // low byte 276 byte high = (byte)(delim >> 8 & 0xFF); // high byte 277 278 int lasPos = 0; 279 for (int i = 0; i < data.length; i += 2) 280 { 281 if (data[i] == low && data[i + 1] == high) 282 { 283 byte[] bytes = new byte[i - lasPos]; 284 System.arraycopy(data, lasPos, bytes, 0, bytes.length); 285 lasPos = i + 2; 286 all.add(new SimpleString(bytes)); 287 } 288 } 289 byte[] bytes = new byte[data.length - lasPos]; 290 System.arraycopy(data, lasPos, bytes, 0, bytes.length); 291 all.add(new SimpleString(bytes)); 292 SimpleString[] parts = new SimpleString[all.size()]; 293 return all.toArray(parts); 294 } 295 } 296 297 /** 298 * checks to see if this SimpleString contains the char parameter passed in 299 * 300 * @param c the char to check for 301 * @return true if the char is found, false otherwise. 302 */ 303 public boolean contains(final char c) 304 { 305 final byte low = (byte)(c & 0xFF); // low byte 306 final byte high = (byte)(c >> 8 & 0xFF); // high byte 307 308 for (int i = 0; i < data.length; i += 2) 309 { 310 if (data[i] == low && data[i + 1] == high) 311 { 312 return true; 313 } 314 } 315 return false; 316 } 317 318 /** 319 * Concatenates a SimpleString and a String 320 * @param toAdd the String to concatenate with. 321 * @return the concatenated SimpleString 322 */ 323 public SimpleString concat(final String toAdd) 324 { 325 return concat(new SimpleString(toAdd)); 326 } 327 328 /** 329 * Concatenates 2 SimpleString's 330 * @param toAdd the SimpleString to concatenate with. 331 * @return the concatenated 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 * Concatenates a SimpleString and a char 343 * @param c the char to concate with. 344 * @return the concatenated SimpleString 345 */ 346 public SimpleString concat(final char c) 347 { 348 byte[] bytes = new byte[data.length + 2]; 349 System.arraycopy(data, 0, bytes, 0, data.length); 350 bytes[data.length] = (byte)(c & 0xFF); 351 bytes[data.length + 1] = (byte)(c >> 8 & 0xFF); 352 return new SimpleString(bytes); 353 } 354 355 /** 356 * returns the size of this SimpleString 357 * @return the size 358 */ 359 public int sizeof() 360 { 361 return DataConstants.SIZE_INT + data.length; 362 } 363 364 /** 365 * returns the size of a SimpleString 366 * @param str the SimpleString to check 367 * @return the size 368 */ 369 public static int sizeofString(final SimpleString str) 370 { 371 return str.sizeof(); 372 } 373 374 /** 375 * returns the size of a SimpleString which could be null 376 * @param str the SimpleString to check 377 * @return the size 378 */ 379 public static int sizeofNullableString(final SimpleString str) 380 { 381 if (str == null) 382 { 383 return 1; 384 } 385 else 386 { 387 return 1 + str.sizeof(); 388 } 389 } 390 391 /** 392 * 393 * @param srcBegin 394 * @param srcEnd 395 * @param dst 396 * @param dstBegin 397 */ 398 public void getChars(final int srcBegin, final int srcEnd, final char dst[], final int dstBegin) 399 { 400 if (srcBegin < 0) 401 { 402 throw new StringIndexOutOfBoundsException(srcBegin); 403 } 404 if (srcEnd > length()) 405 { 406 throw new StringIndexOutOfBoundsException(srcEnd); 407 } 408 if (srcBegin > srcEnd) 409 { 410 throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); 411 } 412 413 int j = 0; 414 415 for (int i = srcBegin; i < srcEnd - srcBegin; i++) 416 { 417 int low = data[j++] & 0xFF; 418 419 int high = data[j++] << 8 & 0xFF00; 420 421 dst[i] = (char)(low | high); 422 } 423 } 424 425}