001 /*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements. See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership. The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied. See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 *
020 */
021
022 package org.apache.qpid.framing;
023
024 import org.apache.mina.common.ByteBuffer;
025
026 import org.slf4j.Logger;
027 import org.slf4j.LoggerFactory;
028
029 import java.util.*;
030 import java.lang.ref.WeakReference;
031
032 /**
033 * A short string is a representation of an AMQ Short String
034 * Short strings differ from the Java String class by being limited to on ASCII characters (0-127)
035 * and thus can be held more effectively in a byte buffer.
036 *
037 */
038 public final class AMQShortString implements CharSequence, Comparable<AMQShortString>
039 {
040 private static final byte MINUS = (byte)'-';
041 private static final byte ZERO = (byte) '0';
042
043
044
045 private final class TokenizerImpl implements AMQShortStringTokenizer
046 {
047 private final byte _delim;
048 private int _count = -1;
049 private int _pos = 0;
050
051 public TokenizerImpl(final byte delim)
052 {
053 _delim = delim;
054 }
055
056 public int countTokens()
057 {
058 if(_count == -1)
059 {
060 _count = 1 + AMQShortString.this.occurences(_delim);
061 }
062 return _count;
063 }
064
065 public AMQShortString nextToken()
066 {
067 if(_pos <= AMQShortString.this.length())
068 {
069 int nextDelim = AMQShortString.this.indexOf(_delim, _pos);
070 if(nextDelim == -1)
071 {
072 nextDelim = AMQShortString.this.length();
073 }
074
075 AMQShortString nextToken = AMQShortString.this.substring(_pos, nextDelim++);
076 _pos = nextDelim;
077 return nextToken;
078 }
079 else
080 {
081 return null;
082 }
083 }
084
085 public boolean hasMoreTokens()
086 {
087 return _pos <= AMQShortString.this.length();
088 }
089 }
090
091 private AMQShortString substring(final int from, final int to)
092 {
093 return new AMQShortString(_data, from+_offset, to+_offset);
094 }
095
096
097 private static final ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>> _localInternMap =
098 new ThreadLocal<Map<AMQShortString, WeakReference<AMQShortString>>>()
099 {
100 protected Map<AMQShortString, WeakReference<AMQShortString>> initialValue()
101 {
102 return new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
103 };
104 };
105
106 private static final Map<AMQShortString, WeakReference<AMQShortString>> _globalInternMap =
107 new WeakHashMap<AMQShortString, WeakReference<AMQShortString>>();
108
109 private static final Logger _logger = LoggerFactory.getLogger(AMQShortString.class);
110
111 private final byte[] _data;
112 private final int _offset;
113 private int _hashCode;
114 private String _asString = null;
115
116 private final int _length;
117 private static final char[] EMPTY_CHAR_ARRAY = new char[0];
118
119 public static final AMQShortString EMPTY_STRING = new AMQShortString((String)null);
120
121 public AMQShortString(byte[] data)
122 {
123
124 _data = data.clone();
125 _length = data.length;
126 _offset = 0;
127 }
128
129 public AMQShortString(byte[] data, int pos)
130 {
131 final int size = data[pos++];
132 final byte[] dataCopy = new byte[size];
133 System.arraycopy(data,pos,dataCopy,0,size);
134 _length = size;
135 _data = dataCopy;
136 _offset = 0;
137 }
138
139 public AMQShortString(String data)
140 {
141 this((data == null) ? EMPTY_CHAR_ARRAY : data.toCharArray());
142 _asString = data;
143 }
144
145 public AMQShortString(char[] data)
146 {
147 if (data == null)
148 {
149 throw new NullPointerException("Cannot create AMQShortString with null char[]");
150 }
151
152 final int length = data.length;
153 final byte[] stringBytes = new byte[length];
154 int hash = 0;
155 for (int i = 0; i < length; i++)
156 {
157 stringBytes[i] = (byte) (0xFF & data[i]);
158 hash = (31 * hash) + stringBytes[i];
159 }
160 _hashCode = hash;
161 _data = stringBytes;
162
163 _length = length;
164 _offset = 0;
165
166 }
167
168 public AMQShortString(CharSequence charSequence)
169 {
170 final int length = charSequence.length();
171 final byte[] stringBytes = new byte[length];
172 int hash = 0;
173 for (int i = 0; i < length; i++)
174 {
175 stringBytes[i] = ((byte) (0xFF & charSequence.charAt(i)));
176 hash = (31 * hash) + stringBytes[i];
177
178 }
179
180 _data = stringBytes;
181 _hashCode = hash;
182 _length = length;
183 _offset = 0;
184
185 }
186
187 private AMQShortString(ByteBuffer data, final int length)
188 {
189 if(data.isDirect() || data.isReadOnly())
190 {
191 byte[] dataBytes = new byte[length];
192 data.get(dataBytes);
193 _data = dataBytes;
194 _offset = 0;
195 }
196 else
197 {
198
199 _data = data.array();
200 _offset = data.arrayOffset() + data.position();
201 data.skip(length);
202
203 }
204 _length = length;
205
206 }
207
208 private AMQShortString(final byte[] data, final int from, final int to)
209 {
210 _offset = from;
211 _length = to - from;
212 _data = data;
213 }
214
215 public AMQShortString shrink()
216 {
217 if(_data.length != _length)
218 {
219 byte[] dataBytes = new byte[_length];
220 System.arraycopy(_data,_offset,dataBytes,0,_length);
221 return new AMQShortString(dataBytes,0,_length);
222 }
223 else
224 {
225 return this;
226 }
227 }
228
229 /**
230 * Get the length of the short string
231 * @return length of the underlying byte array
232 */
233 public int length()
234 {
235 return _length;
236 }
237
238 public char charAt(int index)
239 {
240
241 return (char) _data[_offset + index];
242
243 }
244
245 public CharSequence subSequence(int start, int end)
246 {
247 return new CharSubSequence(start, end);
248 }
249
250 public int writeToByteArray(byte[] encoding, int pos)
251 {
252 final int size = length();
253 encoding[pos++] = (byte) size;
254 System.arraycopy(_data,_offset,encoding,pos,size);
255 return pos+size;
256 }
257
258 public static AMQShortString readFromByteArray(byte[] byteEncodedDestination, int pos)
259 {
260
261
262 final AMQShortString shortString = new AMQShortString(byteEncodedDestination, pos);
263 if(shortString.length() == 0)
264 {
265 return null;
266 }
267 else
268 {
269 return shortString;
270 }
271 }
272
273 public static AMQShortString readFromBuffer(ByteBuffer buffer)
274 {
275 final short length = buffer.getUnsigned();
276 if (length == 0)
277 {
278 return null;
279 }
280 else
281 {
282
283 return new AMQShortString(buffer, length);
284 }
285 }
286
287 public byte[] getBytes()
288 {
289 if(_offset == 0 && _length == _data.length)
290 {
291 return _data.clone();
292 }
293 else
294 {
295 byte[] data = new byte[_length];
296 System.arraycopy(_data,_offset,data,0,_length);
297 return data;
298 }
299 }
300
301 public void writeToBuffer(ByteBuffer buffer)
302 {
303
304 final int size = length();
305 //buffer.setAutoExpand(true);
306 buffer.put((byte) size);
307 buffer.put(_data, _offset, size);
308
309 }
310
311 public boolean endsWith(String s)
312 {
313 return endsWith(new AMQShortString(s));
314 }
315
316
317 public boolean endsWith(AMQShortString otherString)
318 {
319
320 if (otherString.length() > length())
321 {
322 return false;
323 }
324
325
326 int thisLength = length();
327 int otherLength = otherString.length();
328
329 for (int i = 1; i <= otherLength; i++)
330 {
331 if (charAt(thisLength - i) != otherString.charAt(otherLength - i))
332 {
333 return false;
334 }
335 }
336 return true;
337 }
338
339 public boolean startsWith(String s)
340 {
341 return startsWith(new AMQShortString(s));
342 }
343
344 public boolean startsWith(AMQShortString otherString)
345 {
346
347 if (otherString.length() > length())
348 {
349 return false;
350 }
351
352 for (int i = 0; i < otherString.length(); i++)
353 {
354 if (charAt(i) != otherString.charAt(i))
355 {
356 return false;
357 }
358 }
359
360 return true;
361
362 }
363
364 public boolean startsWith(CharSequence otherString)
365 {
366 if (otherString.length() > length())
367 {
368 return false;
369 }
370
371 for (int i = 0; i < otherString.length(); i++)
372 {
373 if (charAt(i) != otherString.charAt(i))
374 {
375 return false;
376 }
377 }
378
379 return true;
380 }
381
382
383 private final class CharSubSequence implements CharSequence
384 {
385 private final int _sequenceOffset;
386 private final int _end;
387
388 public CharSubSequence(final int offset, final int end)
389 {
390 _sequenceOffset = offset;
391 _end = end;
392 }
393
394 public int length()
395 {
396 return _end - _sequenceOffset;
397 }
398
399 public char charAt(int index)
400 {
401 return AMQShortString.this.charAt(index + _sequenceOffset);
402 }
403
404 public CharSequence subSequence(int start, int end)
405 {
406 return new CharSubSequence(start + _sequenceOffset, end + _sequenceOffset);
407 }
408 }
409
410 public char[] asChars()
411 {
412 final int size = length();
413 final char[] chars = new char[size];
414
415 for (int i = 0; i < size; i++)
416 {
417 chars[i] = (char) _data[i + _offset];
418 }
419
420 return chars;
421 }
422
423
424 public String asString()
425 {
426 if (_asString == null)
427 {
428 _asString = new String(asChars());
429 }
430 return _asString;
431 }
432
433 public boolean equals(Object o)
434 {
435
436
437 if(o instanceof AMQShortString)
438 {
439 return equals((AMQShortString)o);
440 }
441 if(o instanceof CharSequence)
442 {
443 return equals((CharSequence)o);
444 }
445
446 if (o == null)
447 {
448 return false;
449 }
450
451 if (o == this)
452 {
453 return true;
454 }
455
456
457 return false;
458
459 }
460
461 public boolean equals(final AMQShortString otherString)
462 {
463 if (otherString == this)
464 {
465 return true;
466 }
467
468 if (otherString == null)
469 {
470 return false;
471 }
472
473 final int hashCode = _hashCode;
474
475 final int otherHashCode = otherString._hashCode;
476
477 if ((hashCode != 0) && (otherHashCode != 0) && (hashCode != otherHashCode))
478 {
479 return false;
480 }
481
482 final int length = _length;
483
484 if(length != otherString._length)
485 {
486 return false;
487 }
488
489
490 final byte[] data = _data;
491
492 final byte[] otherData = otherString._data;
493
494 final int offset = _offset;
495
496 final int otherOffset = otherString._offset;
497
498 if(offset == 0 && otherOffset == 0 && length == data.length && length == otherData.length)
499 {
500 return Arrays.equals(data, otherData);
501 }
502 else
503 {
504 int thisIdx = offset;
505 int otherIdx = otherOffset;
506 for(int i = length; i-- != 0; )
507 {
508 if(!(data[thisIdx++] == otherData[otherIdx++]))
509 {
510 return false;
511 }
512 }
513 }
514
515 return true;
516
517 }
518
519 public boolean equals(CharSequence s)
520 {
521 if(s instanceof AMQShortString)
522 {
523 return equals((AMQShortString)s);
524 }
525
526 if (s == null)
527 {
528 return false;
529 }
530
531 if (s.length() != length())
532 {
533 return false;
534 }
535
536 for (int i = 0; i < length(); i++)
537 {
538 if (charAt(i) != s.charAt(i))
539 {
540 return false;
541 }
542 }
543
544 return true;
545 }
546
547 public int hashCode()
548 {
549 int hash = _hashCode;
550 if (hash == 0)
551 {
552 final int size = length();
553
554 for (int i = 0; i < size; i++)
555 {
556 hash = (31 * hash) + _data[i+_offset];
557 }
558
559 _hashCode = hash;
560 }
561
562 return hash;
563 }
564
565 public void setDirty()
566 {
567 _hashCode = 0;
568 }
569
570 public String toString()
571 {
572 return asString();
573 }
574
575 public int compareTo(AMQShortString name)
576 {
577 if (name == null)
578 {
579 return 1;
580 }
581 else
582 {
583
584 if (name.length() < length())
585 {
586 return -name.compareTo(this);
587 }
588
589 for (int i = 0; i < length(); i++)
590 {
591 final byte d = _data[i+_offset];
592 final byte n = name._data[i+name._offset];
593 if (d < n)
594 {
595 return -1;
596 }
597
598 if (d > n)
599 {
600 return 1;
601 }
602 }
603
604 return (length() == name.length()) ? 0 : -1;
605 }
606 }
607
608
609 public AMQShortStringTokenizer tokenize(byte delim)
610 {
611 return new TokenizerImpl(delim);
612 }
613
614
615 public AMQShortString intern()
616 {
617
618 hashCode();
619
620 Map<AMQShortString, WeakReference<AMQShortString>> localMap =
621 _localInternMap.get();
622
623 WeakReference<AMQShortString> ref = localMap.get(this);
624 AMQShortString internString;
625
626 if(ref != null)
627 {
628 internString = ref.get();
629 if(internString != null)
630 {
631 return internString;
632 }
633 }
634
635
636 synchronized(_globalInternMap)
637 {
638
639 ref = _globalInternMap.get(this);
640 if((ref == null) || ((internString = ref.get()) == null))
641 {
642 internString = shrink();
643 ref = new WeakReference(internString);
644 _globalInternMap.put(internString, ref);
645 }
646
647 }
648 localMap.put(internString, ref);
649 return internString;
650
651 }
652
653 private int occurences(final byte delim)
654 {
655 int count = 0;
656 final int end = _offset + _length;
657 for(int i = _offset ; i < end ; i++ )
658 {
659 if(_data[i] == delim)
660 {
661 count++;
662 }
663 }
664 return count;
665 }
666
667 private int indexOf(final byte val, final int pos)
668 {
669
670 for(int i = pos; i < length(); i++)
671 {
672 if(_data[_offset+i] == val)
673 {
674 return i;
675 }
676 }
677 return -1;
678 }
679
680
681 public static AMQShortString join(final Collection<AMQShortString> terms,
682 final AMQShortString delim)
683 {
684 if(terms.size() == 0)
685 {
686 return EMPTY_STRING;
687 }
688
689 int size = delim.length() * (terms.size() - 1);
690 for(AMQShortString term : terms)
691 {
692 size += term.length();
693 }
694
695 byte[] data = new byte[size];
696 int pos = 0;
697 final byte[] delimData = delim._data;
698 final int delimOffset = delim._offset;
699 final int delimLength = delim._length;
700
701
702 for(AMQShortString term : terms)
703 {
704
705 if(pos!=0)
706 {
707 System.arraycopy(delimData, delimOffset,data,pos, delimLength);
708 pos+=delimLength;
709 }
710 System.arraycopy(term._data,term._offset,data,pos,term._length);
711 pos+=term._length;
712 }
713
714
715
716 return new AMQShortString(data,0,size);
717 }
718
719 public int toIntValue()
720 {
721 int pos = _offset;
722 int val = 0;
723
724
725 boolean isNegative = (_data[pos] == MINUS);
726 if(isNegative)
727 {
728 pos++;
729 }
730
731 final int end = _length + _offset;
732
733 while(pos < end)
734 {
735 int digit = (int) (_data[pos++] - ZERO);
736 if((digit < 0) || (digit > 9))
737 {
738 throw new NumberFormatException("\""+toString()+"\" is not a valid number");
739 }
740 val = val * 10;
741 val += digit;
742 }
743 if(isNegative)
744 {
745 val = val * -1;
746 }
747 return val;
748 }
749
750 public boolean contains(final byte b)
751 {
752 final int end = _length + _offset;
753 for(int i = _offset; i < end; i++)
754 {
755 if(_data[i] == b)
756 {
757 return true;
758 }
759 }
760 return false; //To change body of created methods use File | Settings | File Templates.
761 }
762
763
764 public static void main(String args[])
765 {
766 AMQShortString s = new AMQShortString("a.b.c.d.e.f.g.h.i.j.k");
767 AMQShortString s2 = s.substring(2, 7);
768
769 AMQShortStringTokenizer t = s2.tokenize((byte) '.');
770 while(t.hasMoreTokens())
771 {
772 System.err.println(t.nextToken());
773 }
774 }
775
776 }
|