View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.directory.mavibot.btree.serializer;
21  
22  
23  import java.io.IOException;
24  import java.io.UnsupportedEncodingException;
25  import java.nio.ByteBuffer;
26  import java.util.Comparator;
27  
28  import org.apache.directory.mavibot.btree.comparator.StringComparator;
29  import org.apache.directory.mavibot.btree.util.Strings;
30  
31  
32  /**
33   * The String serializer.
34   * 
35   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
36   */
37  public class StringSerializer extends AbstractElementSerializer<String>
38  {
39      public static final StringSerializer INSTANCE = new StringSerializer();
40  
41  
42      /**
43       * Create a new instance of StringSerializer
44       */
45      public StringSerializer()
46      {
47          super( new StringComparator() );
48      }
49  
50  
51      /**
52       * Create a new instance of StringSerializer with custom comparator
53       */
54      public StringSerializer( Comparator<String> comparator )
55      {
56          super( comparator );
57      }
58  
59  
60      /**
61       * A static method used to deserialize a String from a byte array.
62       * @param in The byte array containing the String
63       * @return A String
64       */
65      public static String deserialize( byte[] in )
66      {
67          return deserialize( in, 0 );
68      }
69  
70  
71      /**
72       * A static method used to deserialize a String from a byte array.
73       * @param in The byte array containing the String
74       * @return A String
75       */
76      public static String deserialize( byte[] in, int start )
77      {
78          int length = IntSerializer.deserialize( in, start );
79  
80          if ( length == 0xFFFFFFFF )
81          {
82              return null;
83          }
84  
85          if ( in.length < length + 4 + start )
86          {
87              throw new RuntimeException( "Cannot extract a String from a buffer with not enough bytes" );
88          }
89  
90          return Strings.utf8ToString( in, start + 4, length );
91      }
92  
93  
94      /**
95       * A method used to deserialize a String from a byte array.
96       * @param in The byte array containing the String
97       * @return A String
98       */
99      public String fromBytes( byte[] in )
100     {
101         return deserialize( in, 0 );
102     }
103 
104 
105     /**
106      * A method used to deserialize a String from a byte array.
107      * @param in The byte array containing the String
108      * @return A String
109      */
110     public String fromBytes( byte[] in, int start )
111     {
112         int length = IntSerializer.deserialize( in, start );
113 
114         if ( length == 0xFFFFFFFF )
115         {
116             return null;
117         }
118 
119         if ( in.length < length + start )
120         {
121             throw new RuntimeException( "Cannot extract a String from a buffer with not enough bytes" );
122         }
123 
124         return Strings.utf8ToString( in, start + 4, length );
125     }
126 
127 
128     /**
129      * Serialize a String. We store the length on 4 bytes, then the String
130      * 
131      * @param buffer the Buffer that will contain the serialized value
132      * @param start the position in the buffer we will store the serialized String
133      * @param value the value to serialize
134      * @return The byte[] containing the serialized String
135      */
136     public static byte[] serialize( byte[] buffer, int start, String element )
137     {
138         int len = -1;
139 
140         if ( element != null )
141         {
142             len = element.length();
143         }
144 
145         switch ( len )
146         {
147             case 0:
148                 buffer[start] = 0x00;
149                 buffer[start + 1] = 0x00;
150                 buffer[start + 2] = 0x00;
151                 buffer[start + 3] = 0x00;
152 
153                 break;
154 
155             case -1:
156                 buffer[start] = ( byte ) 0xFF;
157                 buffer[start + 1] = ( byte ) 0xFF;
158                 buffer[start + 2] = ( byte ) 0xFF;
159                 buffer[start + 3] = ( byte ) 0xFF;
160 
161                 break;
162 
163             default:
164                 try
165                 {
166                     byte[] strBytes = element.getBytes( "UTF-8" );
167 
168                     buffer = new byte[strBytes.length + 4];
169 
170                     System.arraycopy( strBytes, 0, buffer, 4, strBytes.length );
171 
172                     buffer[start] = ( byte ) ( strBytes.length >>> 24 );
173                     buffer[start + 1] = ( byte ) ( strBytes.length >>> 16 );
174                     buffer[start + 2] = ( byte ) ( strBytes.length >>> 8 );
175                     buffer[start + 3] = ( byte ) ( strBytes.length );
176                 }
177                 catch ( UnsupportedEncodingException uee )
178                 {
179                     // if this happens something is really strange
180                     throw new RuntimeException( uee );
181                 }
182         }
183 
184         return buffer;
185     }
186 
187 
188     /**
189      * {@inheritDoc}
190      */
191     public byte[] serialize( String element )
192     {
193         int len = -1;
194 
195         if ( element != null )
196         {
197             len = element.length();
198         }
199 
200         byte[] bytes = null;
201 
202         switch ( len )
203         {
204             case 0:
205                 bytes = new byte[4];
206 
207                 bytes[0] = 0x00;
208                 bytes[1] = 0x00;
209                 bytes[2] = 0x00;
210                 bytes[3] = 0x00;
211 
212                 break;
213 
214             case -1:
215                 bytes = new byte[4];
216 
217                 bytes[0] = ( byte ) 0xFF;
218                 bytes[1] = ( byte ) 0xFF;
219                 bytes[2] = ( byte ) 0xFF;
220                 bytes[3] = ( byte ) 0xFF;
221 
222                 break;
223 
224             default:
225                 char[] chars = element.toCharArray();
226                 byte[] tmpBytes = new byte[chars.length * 2];
227 
228                 int pos = 0;
229                 len = 0;
230 
231                 for ( char c : chars )
232                 {
233                     if ( ( c & 0xFF80 ) == 0 )
234                     {
235                         tmpBytes[pos++] = ( byte ) c;
236                     }
237                     else if ( ( c & 0xF800 ) == 0 )
238                     {
239                         tmpBytes[pos++] = ( byte ) ( ( byte ) 0x00C0 | ( byte ) ( ( c & 0x07C0 ) >> 6 ) );
240                         tmpBytes[pos++] = ( byte ) ( ( byte ) 0x80 | ( byte ) ( c & 0x003F ) );
241                     }
242                     else
243                     {
244                         tmpBytes[pos++] = ( byte ) ( ( byte ) 0x80 | ( byte ) ( c & 0x001F ) );
245                         tmpBytes[pos++] = ( byte ) ( ( byte ) 0x80 | ( byte ) ( c & 0x07C0 ) );
246                         tmpBytes[pos++] = ( byte ) ( ( byte ) 0xE0 | ( byte ) ( c & 0x7800 ) );
247                     }
248                 }
249 
250                 bytes = new byte[pos + 4];
251 
252                 bytes[0] = ( byte ) ( pos >>> 24 );
253                 bytes[1] = ( byte ) ( pos >>> 16 );
254                 bytes[2] = ( byte ) ( pos >>> 8 );
255                 bytes[3] = ( byte ) ( pos );
256 
257                 System.arraycopy( tmpBytes, 0, bytes, 4, pos );
258         }
259 
260         return bytes;
261     }
262 
263 
264     /**
265      * {@inheritDoc}
266      * @throws IOException 
267      */
268     public String deserialize( BufferHandler bufferHandler ) throws IOException
269     {
270         byte[] in = bufferHandler.read( 4 );
271 
272         int len = IntSerializer.deserialize( in );
273 
274         switch ( len )
275         {
276             case 0:
277                 return "";
278 
279             case -1:
280                 return null;
281 
282             default:
283                 in = bufferHandler.read( len );
284 
285                 return Strings.utf8ToString( in );
286         }
287     }
288 
289 
290     /**
291      * {@inheritDoc}
292      */
293     public String deserialize( ByteBuffer buffer ) throws IOException
294     {
295         int len = buffer.getInt();
296 
297         switch ( len )
298         {
299             case 0:
300                 return "";
301 
302             case -1:
303                 return null;
304 
305             default:
306                 byte[] bytes = new byte[len];
307 
308                 buffer.get( bytes );
309                 char[] chars = new char[len];
310                 int clen = 0;
311 
312                 for ( int i = 0; i < len; i++ )
313                 {
314                     byte b = bytes[i];
315 
316                     if ( b >= 0 )
317                     {
318                         chars[clen++] = ( char ) b;
319                     }
320                     else
321                     {
322                         if ( ( b & 0xE0 ) == 0 )
323                         {
324                             // 3 bytes long char
325                             i++;
326                             byte b2 = bytes[i];
327                             i++;
328                             byte b3 = bytes[i];
329                             chars[clen++] = ( char ) ( ( ( b & 0x000F ) << 12 ) | ( ( b2 & 0x003F ) << 6 ) | ( ( b3 & 0x003F ) ) );
330                         }
331                         else
332                         {
333                             // 2 bytes long char
334                             i++;
335                             byte b2 = bytes[i];
336                             chars[clen++] = ( char ) ( ( ( b & 0x001F ) << 6 ) | ( b2 & 0x003F ) );
337                         }
338                     }
339                 }
340 
341                 return new String( chars, 0, clen );
342         }
343     }
344 
345 
346     /**
347      * {@inheritDoc}
348      */
349     @Override
350     public int compare( String type1, String type2 )
351     {
352         if ( type1 == type2 )
353         {
354             return 0;
355         }
356 
357         if ( type1 == null )
358         {
359             if ( type2 == null )
360             {
361                 return 0;
362             }
363             else
364             {
365                 return -1;
366             }
367         }
368         else
369         {
370             if ( type2 == null )
371             {
372                 return 1;
373             }
374             else
375             {
376                 return type1.compareTo( type2 );
377             }
378         }
379     }
380 }