    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] & 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        * This method performs a similar function to {@link String#getChars(int, int, char[], int)}.
    393        * This is mainly used by the Parsers on Filters
    394        *
    395        * @param srcBegin
    396        * @param srcEnd
    397        * @param dst dd
    398        * @param dstPos
    399        */
    400       public void getChars(final int srcBegin, final int srcEnd, final char dst[], final int dstPos)
    401       {
    402          if (srcBegin < 0)
    403          {
    404             throw new StringIndexOutOfBoundsException(srcBegin);
    405          }
    406          if (srcEnd > length())
    407          {
    408             throw new StringIndexOutOfBoundsException(srcEnd);
    409          }
    410          if (srcBegin > srcEnd)
    411          {
    412             throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
    413          }
    414
    415          int j = srcBegin * 2;
    416          int d = dstPos;
    417
    418          for (int i = srcBegin; i < srcEnd; i++)
    419          {
    420             int low = data[j++] & 0xFF;
    421
    422             int high = data[j++] << 8 & 0xFF00;
    423
    424             dst[d++] = (char)(low | high);
    425          }
    426       }
    427
    428    }