AMQShortString.java
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 = + 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++(bytesize;
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 == && _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((bytesize);
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(instanceof AMQShortString)
438         {
439             return equals((AMQShortString)o);
440         }
441         if(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 == && otherOffset == && 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(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()) : -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(27);
768 
769         AMQShortStringTokenizer t = s2.tokenize((byte'.');
770         while(t.hasMoreTokens())
771         {
772             System.err.println(t.nextToken());
773         }
774     }
775 
776 }