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 }