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