001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.mavibot.btree.serializer; 021 022 023import java.io.IOException; 024import java.io.UnsupportedEncodingException; 025import java.nio.ByteBuffer; 026import java.util.Comparator; 027 028import org.apache.directory.mavibot.btree.comparator.StringComparator; 029import org.apache.directory.mavibot.btree.exception.SerializerCreationException; 030import org.apache.directory.mavibot.btree.util.Strings; 031 032 033/** 034 * The String serializer. 035 * 036 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 037 */ 038public class StringSerializer extends AbstractElementSerializer<String> 039{ 040 /** A static instance of a StringSerializer */ 041 public static final StringSerializer INSTANCE = new StringSerializer(); 042 043 /** 044 * Create a new instance of StringSerializer 045 */ 046 private StringSerializer() 047 { 048 super( StringComparator.INSTANCE ); 049 } 050 051 052 /** 053 * Create a new instance of StringSerializer with custom comparator 054 */ 055 public StringSerializer( Comparator<String> comparator ) 056 { 057 super( comparator ); 058 } 059 060 061 /** 062 * A static method used to deserialize a String from a byte array. 063 * @param in The byte array containing the String 064 * @return A String 065 */ 066 public static String deserialize( byte[] in ) 067 { 068 return deserialize( in, 0 ); 069 } 070 071 072 /** 073 * A static method used to deserialize a String from a byte array. 074 * @param in The byte array containing the String 075 * @return A String 076 */ 077 public static String deserialize( byte[] in, int start ) 078 { 079 int length = IntSerializer.deserialize( in, start ); 080 081 if ( length == 0xFFFFFFFF ) 082 { 083 return null; 084 } 085 086 if ( in.length < length + 4 + start ) 087 { 088 throw new SerializerCreationException( "Cannot extract a String from a buffer with not enough bytes" ); 089 } 090 091 return Strings.utf8ToString( in, start + 4, length ); 092 } 093 094 095 /** 096 * A method used to deserialize a String from a byte array. 097 * @param in The byte array containing the String 098 * @return A String 099 */ 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}