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