package me.prettyprint.cassandra.serializers;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import me.prettyprint.hector.api.exceptions.HectorSerializationException;

import org.junit.Test;

/**
 *
 * @author Bozhidar Bozhanov
 *
 */
public class ObjectSerializerTest {

  @Test
  public void testConversions() {
    test(new SampleObject());
    test("Test string");
    test(Integer.MAX_VALUE);
    test(Long.MIN_VALUE);
    test(null);
  }

  @Test
  public void testEmptyByteArray_shouldReturnNull() {
    ObjectSerializer ser = ObjectSerializer.get();
    assertNull("An empty byte array should be translated to null", ser.fromByteBuffer(ByteBuffer.wrap(new byte[0])));
  }

  @Test(expected = HectorSerializationException.class)
  public void testMalformedObject_shouldThrow() {
    ObjectSerializer ser = ObjectSerializer.get();
    ser.fromByteBuffer(ByteBuffer.wrap(new byte[]{1, 2, 3}));
  }

  @Test
  public void testCustomClassLoader() throws Exception {
    ClassLoader bootstrapClassLoader = ClassLoader.getSystemClassLoader().getParent();
    
    //create a new class loader which has the same urls as the class loader used to load this class
    //the new class loader will create classes that are independent of this class
    URLClassLoader thisClassLoader = (URLClassLoader) SampleObject.class.getClassLoader();
    ClassLoader customClassLoader = new URLClassLoader(thisClassLoader.getURLs() , bootstrapClassLoader);
    
    //load the SampleObjectClass from the other class loader
    Class<?> sampleObjectClassOtherClassLoader = customClassLoader.loadClass(SampleObject.class.getName());
    //get a constructor we can access, we are not able to see the default
    //constructor without going through some hoops as we are no longer
    //the same class
    Constructor<?> constructor = sampleObjectClassOtherClassLoader.getDeclaredConstructors()[0];
    constructor.setAccessible(true);
    //create the object
    //this is an instance of SampleObject, but from another class loader, so
    //we can't assign it to a variable of type SampleObject
    Object sampleObjectOtherCl = constructor.newInstance();
    
    ObjectSerializer ser = new ObjectSerializer(customClassLoader);
    Object deserialized = ser.fromByteBuffer(ser.toByteBuffer(sampleObjectOtherCl));
    
    assertFalse(deserialized.getClass() == SampleObject.class);
    assertTrue(deserialized.getClass() == sampleObjectClassOtherClassLoader);
    assertEquals(sampleObjectOtherCl, deserialized);
  }
  
  private void test(Object object) {
    ObjectSerializer ser = ObjectSerializer.get();
    assertEquals(object, ser.fromByteBuffer(ser.toByteBuffer(object)));
  }


  @SuppressWarnings("serial")
  private static class SampleObject implements Serializable {
    private final String a = "test";
    private final List<Void> b = new ArrayList<Void>();
    private final Calendar c = Calendar.getInstance();
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + (a == null ? 0 : a.hashCode());
      result = prime * result + (b == null ? 0 : b.hashCode());
      result = prime * result + (c == null ? 0 : c.hashCode());
      return result;
    }
    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (obj == null) {
        return false;
      }
      if (getClass() != obj.getClass()) {
        return false;
      }
      SampleObject other = (SampleObject) obj;
      if (a == null) {
        if (other.a != null) {
          return false;
        }
      } else if (!a.equals(other.a)) {
        return false;
      }
      if (b == null) {
        if (other.b != null) {
          return false;
        }
      } else if (!b.equals(other.b)) {
        return false;
      }
      if (c == null) {
        if (other.c != null) {
          return false;
        }
      } else if (!c.equals(other.c)) {
        return false;
      }
      return true;
    }
  }
}

@@ -1381,29 +1381,25 @@
 etParent();%0A
-    
 %0A
+
     //create
@@ -1765,21 +1765,17 @@
 oader);%0A
-    
 %0A
+
     //lo
@@ -2133,16 +2133,69 @@
 ructor =
+ null;%0A%09final Constructor%3C?%3E%5B%5D declaredConstructors =
  sampleO
@@ -2250,49 +2250,214 @@
 rs()
-%5B0%5D;%0A    constructor.setAccessible(true);
+;%0A%09// find the constructor w/o parameters%0A%09for (Constructor%3C?%3E con : declaredConstructors) %7B%0A%09%09if(con.getParameterTypes().length == 0) %7B%0A%09%09%09con.setAccessible(true);%0A%09%09 %09constructor = con;%0A%09%09  %09break;%0A%09%09%7D%0A%09%7D
 %0A   
@@ -2610,24 +2610,26 @@
 ampleObject%0A
+%0A%0A
     Object s
@@ -2676,21 +2676,17 @@
 ance();%0A
-    
 %0A
+
     Obje
@@ -2834,13 +2834,9 @@
 ));%0A
-    
 %0A
+
     
@@ -3030,18 +3030,16 @@
 d);%0A  %7D%0A
-  
 %0A  priva

Fix Java 7 test issue.