All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.pdx.PdxSerializableJUnitTest Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.pdx;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.NotSerializableException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import junit.framework.TestCase;

import com.gemstone.gemfire.CopyHelper;
import com.gemstone.gemfire.DataSerializable;
import com.gemstone.gemfire.DataSerializer;
import com.gemstone.gemfire.DeltaTestImpl;
import com.gemstone.gemfire.ToDataException;
import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.DiskStore;
import com.gemstone.gemfire.cache.DiskStoreFactory;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionShortcut;
import com.gemstone.gemfire.cache.query.internal.DefaultQuery;
import com.gemstone.gemfire.internal.DSCODE;
import com.gemstone.gemfire.internal.FileUtil;
import com.gemstone.gemfire.internal.HeapDataOutputStream;
import com.gemstone.gemfire.internal.PdxSerializerObject;
import com.gemstone.gemfire.internal.SystemAdmin;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.internal.cache.xmlcache.CacheXmlGenerator;
import com.gemstone.gemfire.internal.shared.Version;
import com.gemstone.gemfire.internal.util.ArrayUtils;
import com.gemstone.gemfire.pdx.internal.DataSize;
import com.gemstone.gemfire.pdx.internal.PdxReaderImpl;
import com.gemstone.gemfire.pdx.internal.PdxType;
import com.gemstone.gemfire.pdx.internal.PdxWriterImpl;
import com.gemstone.gemfire.pdx.internal.TypeRegistry;

public class PdxSerializableJUnitTest extends TestCase {

  
  public PdxSerializableJUnitTest() {
    super();
  }

  private GemFireCacheImpl c;

  @Override
  public void setUp() {
    // make it a loner
    this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
        .create();
  }

  @Override
  public void tearDown() {
    this.c.close();
  }
  
  private int getLastPdxTypeId() {
    return this.c.getPdxRegistry().getLastAllocatedTypeId();
  }

  public void testNoDiskStore() throws Exception {
    this.c.close();
    this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
    .setPdxPersistent(true)
    .setPdxDiskStore("doesNotExist")
    .create();
    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    PdxSerializable object = new SimpleClass(1, (byte)5, null);
    try {
      DataSerializer.writeObject(object, out);
      fail("expected PdxInitializationException");
    } catch (PdxInitializationException expected) {
    }
  }
  
  // for bugs 44271 and 44914
  public void testPdxPersistentKeys() throws Exception {
    this.c.close();
    this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
    .setPdxPersistent(true)
    .setPdxDiskStore("pdxDS")
    .create();
    try {
      DiskStoreFactory dsf = this.c.createDiskStoreFactory();
      dsf.create("pdxDS");
      this.c.createDiskStoreFactory().create("r2DS");
      Region r1 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).create("r1");
      r1.put(new SimpleClass(1, (byte) 1), "1");
      r1.put(new SimpleClass(2, (byte) 2), "2");
      r1.put(new SimpleClass(1, (byte) 1), "1.2"); // so we have something to compact offline
      Region r2 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).setDiskStoreName("r2DS").create("r2");
      r2.put(new SimpleClass(1, (byte) 1), new SimpleClass(1, (byte) 1));
      r2.put(new SimpleClass(2, (byte) 2), new SimpleClass(2, (byte) 2));
      this.c.close();
      this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
      .setPdxPersistent(true)
      .setPdxDiskStore("pdxDS")
      .create();
      dsf = this.c.createDiskStoreFactory();
      dsf.create("pdxDS");
      this.c.createDiskStoreFactory().create("r2DS");
      r1 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).create("r1");
      r2 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).setDiskStoreName("r2DS").create("r2");
      assertEquals(true, r1.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r1.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(true, r2.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r2.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(new SimpleClass(1, (byte) 1), r2.get(new SimpleClass(1, (byte) 1)));
      assertEquals(new SimpleClass(2, (byte) 2), r2.get(new SimpleClass(2, (byte) 2)));
      this.c.close();
      // use a cache.xml to recover
      this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
      .create();
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos), true);
      pw.println("");
      pw.println("");
      pw.println("");
      pw.println("  ");
      pw.println("  ");
      pw.println("  ");
      pw.println("  ");
      pw.println("  ");
      pw.println("    ");
      pw.println("  ");
      pw.println("");
      pw.close();
      byte[] bytes = baos.toByteArray();
      this.c.loadCacheXml(new ByteArrayInputStream(bytes));
      r1 = this.c.getRegion("/r1");
      r2 = this.c.getRegion("/r2");
      assertEquals(true, r1.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r1.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(true, r2.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r2.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(new SimpleClass(1, (byte) 1), r2.get(new SimpleClass(1, (byte) 1)));
      assertEquals(new SimpleClass(2, (byte) 2), r2.get(new SimpleClass(2, (byte) 2)));
      this.c.close();
      // make sure offlines tools work with disk store that has pdx keys
      SystemAdmin admin = new SystemAdmin();
      admin.validateDiskStore("DEFAULT", ".");
      admin.compactDiskStore("DEFAULT", ".");
      admin.modifyDiskStore("DEFAULT", ".");
      admin.validateDiskStore("r2DS", ".");
      admin.compactDiskStore("r2DS", ".");
      admin.modifyDiskStore("r2DS", ".");
      admin.validateDiskStore("pdxDS", ".");
      admin.compactDiskStore("pdxDS", ".");
      admin.modifyDiskStore("pdxDS", ".");
    } finally {
      try {
        this.c.close();
      } finally {
      FileUtil.deleteMatching(new File("."), "BACKUP(DEFAULT|pdxDS|r2DS).*");
      }
    }
  }
  public void testPdxPersistentKeysDefDS() throws Exception {
    this.c.close();
    this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
    .setPdxPersistent(true)
    .create();
    try {
      this.c.createDiskStoreFactory().create("r2DS");
      Region r1 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).setDiskStoreName("r2DS").create("r1");
      r1.put(new SimpleClass(1, (byte) 1), "1");
      r1.put(new SimpleClass(2, (byte) 2), "2");
      r1.put(new SimpleClass(1, (byte) 1), "1.2"); // so we have something to compact offline
      Region r2 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).setDiskStoreName("r2DS").create("r2");
      r2.put(new SimpleClass(1, (byte) 1), new SimpleClass(1, (byte) 1));
      r2.put(new SimpleClass(2, (byte) 2), new SimpleClass(2, (byte) 2));
      this.c.close();
      this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
      .setPdxPersistent(true)
      .create();
      this.c.createDiskStoreFactory().create("r2DS");
      r1 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).setDiskStoreName("r2DS").create("r1");
      r2 = this.c.createRegionFactory(RegionShortcut.LOCAL_PERSISTENT).setDiskStoreName("r2DS").create("r2");
      assertEquals(true, r1.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r1.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(true, r2.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r2.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(new SimpleClass(1, (byte) 1), r2.get(new SimpleClass(1, (byte) 1)));
      assertEquals(new SimpleClass(2, (byte) 2), r2.get(new SimpleClass(2, (byte) 2)));
      this.c.close();
      // use a cache.xml to recover
      this.c = (GemFireCacheImpl) new CacheFactory().set("mcast-port", "0")
      .create();
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      PrintWriter pw = new PrintWriter(new OutputStreamWriter(baos), true);
      pw.println("");
      pw.println("");
      pw.println("");
      pw.println("  ");
      pw.println("  ");
      pw.println("  ");
      pw.println("    ");
      pw.println("  ");
      pw.println("  ");
      pw.println("    ");
      pw.println("  ");
      pw.println("");
      pw.close();
      byte[] bytes = baos.toByteArray();
      this.c.loadCacheXml(new ByteArrayInputStream(bytes));
      r1 = this.c.getRegion("/r1");
      r2 = this.c.getRegion("/r2");
      assertEquals(true, r1.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r1.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(true, r2.containsKey(new SimpleClass(1, (byte) 1)));
      assertEquals(true, r2.containsKey(new SimpleClass(2, (byte) 2)));
      assertEquals(new SimpleClass(1, (byte) 1), r2.get(new SimpleClass(1, (byte) 1)));
      assertEquals(new SimpleClass(2, (byte) 2), r2.get(new SimpleClass(2, (byte) 2)));
      this.c.close();
      // make sure offlines tools work with disk store that has pdx keys
      SystemAdmin admin = new SystemAdmin();
      admin.validateDiskStore("DEFAULT", ".");
      admin.compactDiskStore("DEFAULT", ".");
      admin.modifyDiskStore("DEFAULT", ".");
      admin.validateDiskStore("r2DS", ".");
      admin.compactDiskStore("r2DS", ".");
      admin.modifyDiskStore("r2DS", ".");
    } finally {
      try {
        this.c.close();
      } finally {
      FileUtil.deleteMatching(new File("."), "BACKUP(DEFAULT|r2DS).*");
      }
    }
  }

  public void testByteFormatForSimpleClass() throws Exception {
    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    PdxSerializable object = new SimpleClass(1, (byte)5, null);
    DataSerializer.writeObject(object, out);
    int typeId = getLastPdxTypeId();
    byte[] actual = out.toByteArray();
    byte[] expected = new byte[] {
        DSCODE.PDX, // byte
        0, 0, 0,
        4+1+1, // int - length of byte stream = 4(myInt) + 1(myByte) + 1 (myEnum)
        (byte) (typeId >> 24), (byte) (typeId >> 16), (byte) (typeId >> 8),
        (byte) typeId, // int - typeId
        0, 0, 0, 1, // int - myInt = 1
        5, // byte - myByte = 5   
        DSCODE.NULL
    };

    StringBuffer msg = new StringBuffer("Actual output: ");
    for (byte val : actual) {
      msg.append(val + ", ");
    }
    msg.append("\nExpected output: ");
    for (byte val : expected) {
      msg.append(val + ", ");
    }
    assertTrue("Mismatch in length, actual.length: " + actual.length + " and expected length: " + expected.length, actual.length == expected.length);
    for(int i = 0; i> 24), (byte)(length >> 16), (byte)(length >> 8), (byte)length, // int - length of byte stream
        (byte) (typeId >> 24),
        (byte) (typeId >> 16),
        (byte) (typeId >> 8),
        (byte) typeId, // int - typeId
        1, // boolean - myFlag = true
        (byte)(myShort >> 8), (byte)myShort, // short - myShort
        (byte)(myLong >> 56), (byte)(myLong >> 48), (byte)(myLong >> 40), (byte)(myLong >> 32), (byte)(myLong >> 24), (byte)(myLong >> 16), (byte)(myLong >> 8), (byte)myLong, // long - myLong
        (byte)(myInt >> 24), (byte)(myInt >> 16), (byte)(myInt >> 8), (byte)myInt, // int - myInt
        (byte)(floatBytes >> 24), (byte)(floatBytes >> 16), (byte)(floatBytes >> 8), (byte)floatBytes, // float - myFloat
        (byte)offset3, // offset of myString3
        (byte)offset2, // offset of myString2
    };

    for (int i = (str1Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset1
          + PdxWriterImpl.HEADER_SIZE, str1Bytes[i]); // + 5 for: 1 for
                                                      // DSCODE.PDX and 4 for
                                                      // byte stream length
    }
    for (int i = (str2Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset2
          + PdxWriterImpl.HEADER_SIZE, str2Bytes[i]);
    }
    for (int i = (str3Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset3
          + PdxWriterImpl.HEADER_SIZE, str3Bytes[i]);
    }
    
    StringBuffer msg = new StringBuffer("Actual output: ");
    for (byte val : actual) {
      msg.append(val + ", ");
    }
    msg.append("\nExpected output: ");
    for (byte val : expected) {
      msg.append(val + ", ");
    }
    if (actual.length != expected.length) {
      System.out.println(msg.toString());
    }
    assertTrue("Mismatch in length, actual.length: " + actual.length + " and expected length: " + expected.length, actual.length == expected.length);
    for(int i = 0; i < actual.length; i++) {
      if (actual[i] != expected[i]) {
        System.out.println(msg.toString());
      }
      assertTrue("Mismatch at index " + i, actual[i] == expected[i]);
    }
    System.out.println("\n");

    DataInput in = new DataInputStream(new ByteArrayInputStream(actual));
    SimpleClass1 actualVal = (SimpleClass1)DataSerializer.readObject(in);
    //System.out.println("actualVal..."+actualVal);    
    assertTrue("Mismatch in write and read value: Value Write..." + pdx
        + " Value Read..." + actualVal, pdx.equals(actualVal));    
    
    c.setReadSerialized(true);
    try {
      in = new DataInputStream(new ByteArrayInputStream(actual));
      PdxInstance pi = (PdxInstance)DataSerializer.readObject(in);
      actualVal = (SimpleClass1)pi.getObject();
      assertTrue("Mismatch in write and read value: Value Write..." + pdx
                 + " Value Read..." + actualVal, pdx.equals(actualVal)); 
      assertTrue(pi.hasField("myFlag"));
      assertTrue(pi.hasField("myShort"));
      assertTrue(pi.hasField("myString1"));
      assertTrue(pi.hasField("myLong"));
      assertTrue(pi.hasField("myString2"));
      assertTrue(pi.hasField("myString3"));
      assertTrue(pi.hasField("myInt"));
      assertTrue(pi.hasField("myFloat"));
      assertEquals(pdx.isMyFlag(), pi.getField("myFlag"));
      assertEquals(pdx.getMyShort(), pi.getField("myShort"));
      assertEquals(pdx.getMyString1(), pi.getField("myString1"));
      assertEquals(pdx.getMyLong(), pi.getField("myLong"));
      assertEquals(pdx.getMyString2(), pi.getField("myString2"));
      assertEquals(pdx.getMyString3(), pi.getField("myString3"));
      assertEquals(pdx.getMyInt(), pi.getField("myInt"));
      assertEquals(pdx.getMyFloat(), pi.getField("myFloat"));
      PdxReaderImpl reader = (PdxReaderImpl)pi;
      PdxType type = reader.getPdxType();
      assertEquals(SimpleClass1.class.getName(), type.getClassName());
      assertEquals(8, type.getFieldCount());
      assertEquals(2, type.getVariableLengthFieldCount());

      assertEquals(0, type.getPdxField("myFlag").getFieldIndex());
      assertEquals(1, type.getPdxField("myShort").getFieldIndex());
      assertEquals(2, type.getPdxField("myString1").getFieldIndex());
      assertEquals(3, type.getPdxField("myLong").getFieldIndex());
      assertEquals(4, type.getPdxField("myString2").getFieldIndex());
      assertEquals(5, type.getPdxField("myString3").getFieldIndex());
      assertEquals(6, type.getPdxField("myInt").getFieldIndex());
      assertEquals(7, type.getPdxField("myFloat").getFieldIndex());
      
      assertEquals(FieldType.BOOLEAN, type.getPdxField("myFlag").getFieldType());
      assertEquals(FieldType.SHORT, type.getPdxField("myShort").getFieldType());
      assertEquals(FieldType.STRING, type.getPdxField("myString1").getFieldType());
      assertEquals(FieldType.LONG, type.getPdxField("myLong").getFieldType());
      assertEquals(FieldType.STRING, type.getPdxField("myString2").getFieldType());
      assertEquals(FieldType.STRING, type.getPdxField("myString3").getFieldType());
      assertEquals(FieldType.INT, type.getPdxField("myInt").getFieldType());
      assertEquals(FieldType.FLOAT, type.getPdxField("myFloat").getFieldType());

      assertEquals("myFlag", type.getPdxField("myFlag").getFieldName());
      assertEquals("myShort", type.getPdxField("myShort").getFieldName());
      assertEquals("myString1", type.getPdxField("myString1").getFieldName());
      assertEquals("myLong", type.getPdxField("myLong").getFieldName());
      assertEquals("myString2", type.getPdxField("myString2").getFieldName());
      assertEquals("myString3", type.getPdxField("myString3").getFieldName());
      assertEquals("myInt", type.getPdxField("myInt").getFieldName());
      assertEquals("myFloat", type.getPdxField("myFloat").getFieldName());
      
      assertEquals(0, type.getPdxField("myFlag").getVarLenFieldSeqId());
      assertEquals(0, type.getPdxField("myShort").getVarLenFieldSeqId());
      assertEquals(0, type.getPdxField("myString1").getVarLenFieldSeqId());
      assertEquals(0, type.getPdxField("myLong").getVarLenFieldSeqId());
      assertEquals(1, type.getPdxField("myString2").getVarLenFieldSeqId());
      assertEquals(2, type.getPdxField("myString3").getVarLenFieldSeqId());
      assertEquals(2, type.getPdxField("myInt").getVarLenFieldSeqId());
      assertEquals(2, type.getPdxField("myFloat").getVarLenFieldSeqId());

      assertEquals(false, type.getPdxField("myFlag").isVariableLengthType());
      assertEquals(false, type.getPdxField("myShort").isVariableLengthType());
      assertEquals(true, type.getPdxField("myString1").isVariableLengthType());
      assertEquals(false, type.getPdxField("myLong").isVariableLengthType());
      assertEquals(true, type.getPdxField("myString2").isVariableLengthType());
      assertEquals(true, type.getPdxField("myString3").isVariableLengthType());
      assertEquals(false, type.getPdxField("myInt").isVariableLengthType());
      assertEquals(false, type.getPdxField("myFloat").isVariableLengthType());

      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(pdx.isMyFlag()?1:0)}), 
                   reader.getRaw(0));
      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(pdx.getMyShort() >> 8), (byte)pdx.getMyShort()}),
                   reader.getRaw(1));
      assertEquals(ByteBuffer.wrap(str1Bytes),
                   reader.getRaw(2));
      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(pdx.getMyLong() >> 56), (byte)(pdx.getMyLong() >> 48), (byte)(pdx.getMyLong() >> 40), (byte)(pdx.getMyLong() >> 32), (byte)(pdx.getMyLong() >> 24), (byte)(pdx.getMyLong() >> 16), (byte)(pdx.getMyLong() >> 8), (byte)pdx.getMyLong(),}), 
                   reader.getRaw(3));
      assertEquals(ByteBuffer.wrap(str2Bytes),
                   reader.getRaw(4));
      assertEquals(ByteBuffer.wrap(str3Bytes),
                   reader.getRaw(5));
      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(pdx.getMyInt() >> 24), (byte)(pdx.getMyInt() >> 16), (byte)(pdx.getMyInt() >> 8), (byte)pdx.getMyInt()}),
                   reader.getRaw(6));
      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(floatBytes >> 24), (byte)(floatBytes >> 16), (byte)(floatBytes >> 8), (byte)floatBytes}),
                   reader.getRaw(7));
    } finally {
      c.setReadSerialized(false);
    }
  }


  public void testByteFormatForLongStrings() throws Exception {
    boolean myFlag = true;
    short myShort = 25;
    String myString1 = "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1."
        + "A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.A very long string1.";
    long myLong = 15654;
    String myString2 = "Class4_myString2";
    String myString3 = "Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. "
        + "Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. "
        + "Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. "
        + "Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. "
        + "Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. "
        + "Even longer string3. Even longer string3. Even longer string3. Even longer string3. Even longer string3. ";
    int myInt = 1420;
    float myFloat = 123.023f;
    
    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    SimpleClass1 pdx = new SimpleClass1(myFlag, myShort, myString1, myLong, myString2, myString3, myInt, myFloat);
    DataSerializer.writeObject(pdx, out);
    int typeId = getLastPdxTypeId();

    HeapDataOutputStream hdos1 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString1, hdos1);
    byte[] str1Bytes = hdos1.toByteArray();
    HeapDataOutputStream hdos2 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString2, hdos2);
    byte[] str2Bytes = hdos2.toByteArray();
    HeapDataOutputStream hdos3 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString3, hdos3);
    byte[] str3Bytes = hdos3.toByteArray();

    int length = 1 /* myFlag */+ 2 /* myShort */
        + 8 /* myLong */+ 4 /* myInt */+ 4 /* myFloat */+ str1Bytes.length
        + str2Bytes.length + str3Bytes.length + (2*2) /* short offset for 3 strings */;

    int offset1 = 1 + 2;
    int offset2 = offset1 + 8 + str1Bytes.length;
    int offset3 = offset1 + 8 + str1Bytes.length + str2Bytes.length;
    byte[] actual = out.toByteArray();
    int floatBytes = Float.floatToRawIntBits(myFloat);
    Byte[] expected = new Byte[] {
        DSCODE.PDX, // byte
        (byte)(length >> 24), (byte)(length >> 16), (byte)(length >> 8), (byte)length, // int - length of byte stream
        (byte) (typeId >> 24),
        (byte) (typeId >> 16),
        (byte) (typeId >> 8),
        (byte) typeId, // int - typeId
        1, // boolean - myFlag = true
        (byte)(myShort >> 8), (byte)myShort, // short - myShort
        (byte)(myLong >> 56), (byte)(myLong >> 48), (byte)(myLong >> 40), (byte)(myLong >> 32), (byte)(myLong >> 24), (byte)(myLong >> 16), (byte)(myLong >> 8), (byte)myLong, // long - myLong
        (byte)(myInt >> 24), (byte)(myInt >> 16), (byte)(myInt >> 8), (byte)myInt, // int - myInt
        (byte)(floatBytes >> 24), (byte)(floatBytes >> 16), (byte)(floatBytes >> 8), (byte)floatBytes, // float - myFloat
        (byte)(offset3 >> 8), (byte)offset3, // offset of myString3
        (byte)(offset2 >> 8), (byte)offset2, // offset of myString2
    };

    for (int i = (str1Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset1
          + PdxWriterImpl.HEADER_SIZE, str1Bytes[i]); // + 5 for: 1 for
                                                      // DSCODE.PDX and 4 for
                                                      // byte stream length
    }
    for (int i = (str2Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset2
          + PdxWriterImpl.HEADER_SIZE, str2Bytes[i]);
    }
    for (int i = (str3Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset3
          + PdxWriterImpl.HEADER_SIZE, str3Bytes[i]);
    }
    
    StringBuffer msg = new StringBuffer("Actual output: ");
    for (byte val : actual) {
      msg.append(val + ", ");
    }
    msg.append("\nExpected output: ");
    for (byte val : expected) {
      msg.append(val + ", ");
    }
    if (actual.length != expected.length) {
      System.out.println(msg.toString());
    }
    assertTrue("Mismatch in length, actual.length: " + actual.length + " and expected length: " + expected.length, actual.length == expected.length);
    for(int i = 0; i> 8), (byte)pdx.getMyShort()}),
                   reader.getRaw(1));
      assertEquals(ByteBuffer.wrap(str1Bytes),
                   reader.getRaw(2));
      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(pdx.getMyLong() >> 56), (byte)(pdx.getMyLong() >> 48), (byte)(pdx.getMyLong() >> 40), (byte)(pdx.getMyLong() >> 32), (byte)(pdx.getMyLong() >> 24), (byte)(pdx.getMyLong() >> 16), (byte)(pdx.getMyLong() >> 8), (byte)pdx.getMyLong(),}), 
                   reader.getRaw(3));
      assertEquals(ByteBuffer.wrap(str2Bytes),
                   reader.getRaw(4));
      assertEquals(ByteBuffer.wrap(str3Bytes),
                   reader.getRaw(5));
      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(pdx.getMyInt() >> 24), (byte)(pdx.getMyInt() >> 16), (byte)(pdx.getMyInt() >> 8), (byte)pdx.getMyInt()}),
                   reader.getRaw(6));
      assertEquals(ByteBuffer.wrap(new byte[] {(byte)(floatBytes >> 24), (byte)(floatBytes >> 16), (byte)(floatBytes >> 8), (byte)floatBytes}),
                   reader.getRaw(7));
    } finally {
      c.setReadSerialized(false);
    }
    }


  public void testByteFormatForNestedPDX() throws Exception {
    String myString1 = "ComplexClass1_myString1";
    long myLong = 15654;
    HashMap myHashMap = new HashMap();
    String myString2 = "ComplexClass1_myString2";
    float myFloat = 123.023f;

    for (int i = 0; i < 5; i++) {
      myHashMap.put("KEY_"+i, new SimpleClass(i, (byte)5));
    }
    
    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    PdxSerializable pdx = new NestedPdx(myString1, myLong, myHashMap, myString2, myFloat);
    DataSerializer.writeObject(pdx, out);
    int typeId = getLastPdxTypeId();

    HeapDataOutputStream hdos1 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString1, hdos1);
    byte[] str1Bytes = hdos1.toByteArray();
    HeapDataOutputStream hdosForMap = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeObject(myHashMap, hdosForMap);
    byte[] mapBytes = hdosForMap.toByteArray();
    HeapDataOutputStream hdos2 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString2, hdos2);
    byte[] str2Bytes = hdos2.toByteArray();

    int length = str1Bytes.length + 8 /* myLong */
        + mapBytes.length + str2Bytes.length + 4 /* myFloat */+ (2*1) /*
                                                                   * offset for
                                                                   * 3
                                                                   * var-length
                                                                   * fields
                                                                   */;

    int offset1 = 0;
    int offset2 = offset1 + 8 + str1Bytes.length;
    int offset3 = offset1 + 8 + str1Bytes.length + mapBytes.length;
    byte[] actual = out.toByteArray();
    int floatBytes = Float.floatToRawIntBits(myFloat);
    Byte[] expected = new Byte[] {
        DSCODE.PDX, // byte
        (byte)(length >> 24), (byte)(length >> 16), (byte)(length >> 8), (byte)length, // int - length of byte stream
        (byte) (typeId >> 24),
        (byte) (typeId >> 16),
        (byte) (typeId >> 8),
        (byte) typeId, // int - typeId
        (byte)(myLong >> 56), (byte)(myLong >> 48), (byte)(myLong >> 40), (byte)(myLong >> 32), (byte)(myLong >> 24), (byte)(myLong >> 16), (byte)(myLong >> 8), (byte)myLong, // long - myLong
        (byte)(floatBytes >> 24), (byte)(floatBytes >> 16), (byte)(floatBytes >> 8), (byte)floatBytes, // float - myFloat
        (byte)offset3, // offset of myString2
        (byte)offset2, // offset of myHashMap
    };

    for (int i = (str1Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset1
          + PdxWriterImpl.HEADER_SIZE, str1Bytes[i]);
    }
    for (int i = (mapBytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset2
          + PdxWriterImpl.HEADER_SIZE, mapBytes[i]);
    }
    for (int i = (str2Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset3
          + PdxWriterImpl.HEADER_SIZE, str2Bytes[i]);
    }

    StringBuffer msg = new StringBuffer("Actual output: ");
    for (byte val : actual) {
      msg.append(val + ", ");
    }
      msg.append("\nExpected output: ");
        for (byte val : expected) {
          msg.append(val + ", ");
      }
        if (actual.length != expected.length) {
          System.out.println(msg.toString());
        }
      assertTrue("Mismatch in length, actual.length: " + actual.length
          + " and expected length: " + expected.length,
          actual.length == expected.length);
      for (int i = 0; i < actual.length; i++) {
        if (actual[i] != expected[i]) {
          System.out.println(msg.toString());
        }
       assertTrue("Mismatch at index " + i, actual[i] == expected[i]);
      }
    System.out.println("\n");
    DataInput in = new DataInputStream(new ByteArrayInputStream(actual));
    NestedPdx actualVal = (NestedPdx)DataSerializer.readObject(in);       
    //System.out.println("actualVal..."+actualVal);
    assertTrue("Mismatch in write and read value: Value Write..." + pdx
        + " Value Read..." + actualVal, pdx.equals(actualVal));    
    System.out.println("\n");
  }

  public void testByteFormatForDSInsidePDX() throws Exception {
    String myString1 = "ComplexClass1_myString1";
    long myLong = 15654;
    DataSerializable myDS = new DeltaTestImpl(100, "value");
    String myString2 = "ComplexClass1_myString2";
    float myFloat = 123.023f;

    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    PdxSerializable pdx = new DSInsidePdx(myString1, myLong, myDS, myString2, myFloat);
    DataSerializer.writeObject(pdx, out);
    int typeId = getLastPdxTypeId();

    HeapDataOutputStream hdos1 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString1, hdos1);
    byte[] str1Bytes = hdos1.toByteArray();
    System.out.println("Length of string1: " + str1Bytes.length);

    HeapDataOutputStream hdos2 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeObject(myDS, hdos2);
    byte[] dsBytes = hdos2.toByteArray();
    System.out.println("Length of DS: " + dsBytes.length);

    HeapDataOutputStream hdos3 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString2, hdos3);
    byte[] str2Bytes = hdos3.toByteArray();
    System.out.println("Length of string2: " + str2Bytes.length);

    int length = str1Bytes.length + 8 /* myLong */
        + dsBytes.length + str2Bytes.length + 4 /* myFloat */+ (2*2) /*
                                                                  * offset for 3
                                                                  * car-length
                                                                  * fields
                                                                  */;

    int offset1 = 0;
    int offset2 = offset1 + 8 + str1Bytes.length;
    int offset3 = offset1 + 8 + str1Bytes.length + dsBytes.length;
    byte[] actual = out.toByteArray();
    int floatBytes = Float.floatToRawIntBits(myFloat);
    Byte[] expected = new Byte[] {
        DSCODE.PDX, // byte
        (byte)(length >> 24), (byte)(length >> 16), (byte)(length >> 8), (byte)length, // int - length of byte stream
        (byte) (typeId >> 24),
        (byte) (typeId >> 16),
        (byte) (typeId >> 8),
        (byte) typeId, // int - typeId
        (byte)(myLong >> 56), (byte)(myLong >> 48), (byte)(myLong >> 40), (byte)(myLong >> 32), (byte)(myLong >> 24), (byte)(myLong >> 16), (byte)(myLong >> 8), (byte)myLong, // long - myLong
        (byte)(floatBytes >> 24), (byte)(floatBytes >> 16), (byte)(floatBytes >> 8), (byte)floatBytes, // float - myFloat
        (byte)(offset3 >> 8), (byte)offset3, // offset of myString2
        (byte)(offset2 >> 8), (byte)offset2, // offset of myHashMap
    };

    for (int i = (str1Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset1
          + PdxWriterImpl.HEADER_SIZE, str1Bytes[i]); // + 5 for: 1 for
                                                      // DSCODE.PDX and 4 for
                                                      // byte stream length
    }
    for (int i = (dsBytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset2
          + PdxWriterImpl.HEADER_SIZE, dsBytes[i]);
    }
    for (int i = (str2Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[]) ArrayUtils.insert(expected, offset3
          + PdxWriterImpl.HEADER_SIZE, str2Bytes[i]);
    }

    StringBuffer msg = new StringBuffer("Actual output: ");
    for (byte val : actual) {
      msg.append(val + ", ");
    }
    msg.append("\nExpected output: ");
    for (byte val : expected) {
      msg.append(val + ", ");
    }
    assertTrue("Mismatch in length, actual.length: " + actual.length + " and expected length: " + expected.length, actual.length == expected.length);
    for(int i = 0; i myHashMap = new HashMap();
    for (int i = 0; i < 2; i++) {
      myHashMap.put("KEY_"+i, new SimpleClass(i,(byte)i));
    }
    PdxSerializable myPDX = new NestedPdx(myString1, myLong, myHashMap, myString2, myFloat);
    
    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    DataSerializable ds = new PdxInsideDS(myString1, myLong, myPDX, myString2);
    DataSerializer.writeObject(ds, out);

    HeapDataOutputStream hdosString1 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString1, hdosString1);
    byte[] str1Bytes = hdosString1.toByteArray();
    System.out.println("Length of string1: " + str1Bytes.length);

    HeapDataOutputStream hdosMyPDX = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeObject(myPDX, hdosMyPDX);
    byte[] pdxBytes = hdosMyPDX.toByteArray();
    System.out.println("Length of myPDX: " + pdxBytes.length);

    HeapDataOutputStream hdosString2 = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeString(myString2, hdosString2);
    byte[] str2Bytes = hdosString2.toByteArray();
    System.out.println("Length of string2: " + str2Bytes.length);

    Class classInstance = ds.getClass();
    HeapDataOutputStream hdosClass = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeClass(classInstance, hdosClass);
    byte[] dsInitBytes = hdosClass.toByteArray();

    int offsetStr1 = 1 + dsInitBytes.length;
    int offsetPDX = 1 + dsInitBytes.length + str1Bytes.length + 8;
    int offsetStr2 =  1 + dsInitBytes.length + str1Bytes.length + 8 + pdxBytes.length;

    byte[] actual = out.toByteArray();
    int floatBytes = Float.floatToRawIntBits(myFloat);
    Byte[] expected = new Byte[] {
        DSCODE.DATA_SERIALIZABLE, // byte
        (byte)(myLong >> 56), (byte)(myLong >> 48), (byte)(myLong >> 40), (byte)(myLong >> 32), (byte)(myLong >> 24), (byte)(myLong >> 16), (byte)(myLong >> 8), (byte)myLong, // long - myLong
    };

    for (int i = (dsInitBytes.length - 1); i >= 0; i--) {
      expected = (Byte[])ArrayUtils.insert(expected, 1, dsInitBytes[i]);
    }
    for (int i = (str1Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[])ArrayUtils.insert(expected, offsetStr1, str1Bytes[i]);
    }
    for (int i = (pdxBytes.length - 1); i >= 0; i--) {
      expected = (Byte[])ArrayUtils.insert(expected, offsetPDX, pdxBytes[i]);
    }
    for (int i = (str2Bytes.length - 1); i >= 0; i--) {
      expected = (Byte[])ArrayUtils.insert(expected, offsetStr2, str2Bytes[i]);
    }

    checkBytes(expected, actual);

    DataInput in = new DataInputStream(new ByteArrayInputStream(actual));    
    PdxInsideDS actualVal = (PdxInsideDS)DataSerializer.readObject(in);
//    System.out.println("actualVal..."+actualVal);
    assertTrue("Mismatch in write and read value: Value Write..." + ds
        + " Value Read..." + actualVal, ds.equals(actualVal));
  } 
 
  private void checkBytes(Byte[] expected, byte[] actual) {
    StringBuffer msg = new StringBuffer("Actual output: ");
    for (byte val : actual) {
      msg.append(val + ", ");
    }
    msg.append("\nExpected output: ");
    for (byte val : expected) {
      msg.append(val + ", ");
    }
    if (actual.length != expected.length) {
      System.out.println(msg.toString());
    }
    assertTrue("Mismatch in length, actual.length: " + actual.length + " and expected length: " + expected.length, actual.length == expected.length);
    for(int i = 0; i>> 32));
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      LongFieldHolder other = (LongFieldHolder) obj;
      if (v != other.v)
        return false;
      return true;
    }

    public LongFieldHolder(long v) {
      this.v = v;
    }

    public LongFieldHolder() {

    }

    public long getLong() {
      return this.v;
    }

    public void toData(PdxWriter writer) {
      writer.writeLong("f1", this.v);
    }

    public void fromData(PdxReader reader) {
      this.v = (Long) reader.readField("f1");
    }
  }

  public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY
  }

  public static class BasicAllFieldTypes implements PdxSerializerObject {

    public static String FILE_NAME = "testFile.txt";

    // additional fields from tests.util.ValueHolder
    public char aChar;
    public boolean aBoolean;
    public byte aByte;
    public short aShort;
    public int anInt;
    public long aLong;
    public float aFloat;
    public double aDouble;
    public Date aDate;
    public String aString;
    public Object anObject;
    public Map aMap;
    public Collection aCollection;
    public boolean[] aBooleanArray;
    public char[] aCharArray;
    public byte[] aByteArray;
    public short[] aShortArray;
    public int[] anIntArray;
    public long[] aLongArray;
    public float[] aFloatArray;
    public double[] aDoubleArray;
    public String[] aStringArray;
    public Object[] anObjectArray;
    public byte[][] anArrayOfByteArray;

    public BasicAllFieldTypes() {
    }

    public BasicAllFieldTypes(long base, boolean nulls) {
      fillInBaseValues(base, nulls);
    }


    /**
     * Init fields using base as appropriate, but does not initialize
     * aQueryObject field.
     * 
     * @param base
     *          The base value for all fields. Fields are all set to a value
     *          using base as appropriate.
     * @param useNulls
     *          If true, fill in nulls for those fields that can be set to null
     *          (to test null values), if false, then use a valid value (to test
     *          non-null values).
     */
    public void fillInBaseValues(long base, boolean useNulls) {
      final int maxCollectionSize = 100;
      aChar = (char) base;
      aBoolean = true;
      aByte = (byte) base;
      aShort = (short) base;
      anInt = (int) base;
      aLong = base;
      aFloat = base;
      aDouble = base;
      aDate = new Date(base);
      aString = "" + base;
      if (useNulls) {
        aDate = null;
        aString = null;
        anObject = null;
        aMap = null;
        aCollection = null;
        // aRegion = null;
        aBooleanArray = null;
        aCharArray = null;
        aByteArray = null;
        aShortArray = null;
        anIntArray = null;
        aLongArray = null;
        aFloatArray = null;
        aDoubleArray = null;
        anObjectArray = null;
        anArrayOfByteArray = null;
      } else {
        int desiredCollectionSize = (int) Math.min(base, maxCollectionSize);
        anObject = new SimpleClass((int) base, (byte) base);
        aMap = new HashMap();
        aCollection = new ArrayList();
        aBooleanArray = new boolean[desiredCollectionSize];
        aCharArray = new char[desiredCollectionSize];
        aByteArray = new byte[desiredCollectionSize];
        aShortArray = new short[desiredCollectionSize];
        anIntArray = new int[desiredCollectionSize];
        aLongArray = new long[desiredCollectionSize];
        aFloatArray = new float[desiredCollectionSize];
        aDoubleArray = new double[desiredCollectionSize];
        aStringArray = new String[desiredCollectionSize];
        anObjectArray = new Object[desiredCollectionSize];
        anArrayOfByteArray = new byte[desiredCollectionSize][desiredCollectionSize];
        if (desiredCollectionSize > 0) {
          for (int i = 0; i < desiredCollectionSize - 1; i++) {
            aMap.put(i, i);
            aCollection.add(i);
            aBooleanArray[i] = (i % 2) == 0;
            aCharArray[i] = (char) (i);
            aByteArray[i] = (byte) (i);
            aShortArray[i] = (short) i;
            anIntArray[i] = i;
            aLongArray[i] = i;
            aFloatArray[i] = i;
            aDoubleArray[i] = i;
            aStringArray[i] = "" + i;
            anObjectArray[i] = anObject;
            anArrayOfByteArray[i] = aByteArray;
          }
          // now add the last value, using base if possible
          aMap.put(base, base);
          aCollection.add(base);
          aBooleanArray[desiredCollectionSize - 1] = true;
          aCharArray[desiredCollectionSize - 1] = (char) base;
          aByteArray[desiredCollectionSize - 1] = (byte) base;
          aShortArray[desiredCollectionSize - 1] = (short) base;
          anIntArray[desiredCollectionSize - 1] = (int) base;
          aLongArray[desiredCollectionSize - 1] = base;
          aFloatArray[desiredCollectionSize - 1] = base;
          aDoubleArray[desiredCollectionSize - 1] = base;
          aStringArray[desiredCollectionSize - 1] = "" + base;
          anObjectArray[desiredCollectionSize - 1] = anObject;
          // aRegion = CacheHelper.getCache().rootRegions().iterator().next();
        }
      }
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
      return getClass().getName() + " [aChar=" + this.aChar + ", aBoolean="
          + this.aBoolean + ", aByte=" + this.aByte + ", aShort=" + this.aShort
          + ", anInt=" + this.anInt + ", aLong=" + this.aLong + ", aFloat="
          + this.aFloat + ", aDouble=" + this.aDouble + ", aDate=" + this.aDate
          + ", aString=" + this.aString
          + ", anObject=" + this.anObject + ", aMap=" + this.aMap
          + ", aCollection=" + this.aCollection 
          + ", aBooleanArray="
          + Arrays.toString(this.aBooleanArray) + ", aCharArray="
          + Arrays.toString(this.aCharArray) + ", aByteArray="
          + Arrays.toString(this.aByteArray) + ", aShortArray="
          + Arrays.toString(this.aShortArray) + ", anIntArray="
          + Arrays.toString(this.anIntArray) + ", aLongArray="
          + Arrays.toString(this.aLongArray) + ", aFloatArray="
          + Arrays.toString(this.aFloatArray) + ", aDoubleArray="
          + Arrays.toString(this.aDoubleArray) + ", aStringArray="
          + Arrays.toString(this.aStringArray) + ", anObjectArray="
          + Arrays.toString(this.anObjectArray) + ", anArrayOfByteArray="
          + Arrays.toString(this.anArrayOfByteArray) + "]";
    }

    @Override
    public int hashCode() {
      return 37;
    }

    @Override
    public boolean equals(Object o) {
      if (!(o instanceof BasicAllFieldTypes)) {
        return false;
      }
      BasicAllFieldTypes other = (BasicAllFieldTypes) o;
      if (this.aChar != other.aChar) {
        System.out.println("!= aChar");
        return false;
      }
      if (this.aByte != other.aByte) {
        System.out.println("!= aByte");
        return false;
      }
      if (this.aBoolean != other.aBoolean) {
        System.out.println("!= aBoolean");
        return false;
      }
      if (this.aShort != other.aShort) {
        System.out.println("!= aShort");
        return false;
      }
      if (this.anInt != other.anInt) {
        System.out.println("!= anInt");
        return false;
      }
      if (this.aLong != other.aLong) {
        System.out.println("!= aLong");
        return false;
      }
      if (this.aFloat != other.aFloat) {
        System.out.println("!= aFloat");
        return false;
      }
      if (this.aDouble != other.aDouble) {
        System.out.println("!= aDouble");
        return false;
      }
      if (!basicEquals(this.aDate, other.aDate)) {
        System.out.println("!= aDate");
        return false;
      }
      if (!basicEquals(this.aString, other.aString)) {
        System.out.println("!= aString");
        return false;
      }
      if (!basicEquals(this.anObject, other.anObject)) {
        System.out.println("!= nObject");
        return false;
      }
      if (!basicEquals(this.aMap, other.aMap)) {
        System.out.println("!= aMap");
        return false;
      }
      if (!basicEquals(this.aCollection, other.aCollection)) {
        System.out.println("!= aCollection");
        return false;
      }
      // if (!basicEquals(this.aRegion, other.aRegion)) {
      // return false;
      // }
      if (!Arrays.equals(this.aBooleanArray, other.aBooleanArray)) {
        System.out.println("!= boolean[]");
        return false;
      }
      if (!Arrays.equals(this.aCharArray, other.aCharArray)) {
        System.out.println("!= char[]");
        return false;
      }
      if (!Arrays.equals(this.aByteArray, other.aByteArray)) {
        System.out.println("!= byte[]");
        return false;
      }
      if (!Arrays.equals(this.aShortArray, other.aShortArray)) {
        System.out.println("!= short[]");
        return false;
      }
      if (!Arrays.equals(this.anIntArray, other.anIntArray)) {
        System.out.println("!= int[]");
        return false;
      }
      if (!Arrays.equals(this.aLongArray, other.aLongArray)) {
        System.out.println("!= long[]");
        return false;
      }
      if (!Arrays.equals(this.aFloatArray, other.aFloatArray)) {
        System.out.println("!= float[]");
        return false;
      }
      if (!Arrays.equals(this.aDoubleArray, other.aDoubleArray)) {
        System.out.println("!= double[]");
        return false;
      }
      if (!Arrays.equals(this.aStringArray, other.aStringArray)) {
        System.out.println("!= String[]");
        return false;
      }
      if (!Arrays.equals(this.anObjectArray, other.anObjectArray)) {
        System.out.println("!= Object[]");
        return false;
      }
      if (!byteArrayofArraysEqual(this.anArrayOfByteArray,
          other.anArrayOfByteArray)) {
        System.out.println("!= byte[][]");
        return false;
      }
      return true;
    }

    private boolean byteArrayofArraysEqual(byte[][] o1, byte[][] o2) {
      if (o1 == null) {
        return o2 == null;
      } else if (o2 == null) {
        return false;
      } else {
        if (o1.length != o2.length) {
          return false;
        }
        for (int i = 0; i < o1.length; i++) {
          if (!Arrays.equals(o1[i], o2[i])) {
            return false;
          }
        }
      }
      return true;
    }

    private boolean basicEquals(Object o1, Object o2) {
      if (o1 == null) {
        return o2 == null;
      } else if (o2 == null) {
        return false;
      } else {
        return o1.equals(o2);
      }
    }

  }

  public void testVariableFields() throws Exception {
    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeObject(new VariableFields(1), out);
    try {
      DataSerializer.writeObject(new VariableFields(2), out);
      fail("expected ToDataException");
    } catch (ToDataException expected) {
      if (!(expected.getCause() instanceof PdxSerializationException)) {
        fail("expected cause to be PdxSerializationException");
      }
    }
    try {
      DataSerializer.writeObject(new VariableFields(0), out);
      fail("expected PdxSerializationException");
    } catch (PdxSerializationException expected) {
    }
    try {
      DataSerializer.writeObject(new VariableFields(1, Object.class), out);
      fail("expected ToDataException");
    } catch (ToDataException expected) {
      if (!(expected.getCause() instanceof PdxSerializationException)) {
        fail("expected cause to be PdxSerializationException");
      }
    }
    // TODO also test marking different identity fields.
  }
  
  public static class VariableFields implements PdxSerializable {

    private Class fieldType = String.class;
    private int fieldCount;

    public VariableFields(int fieldCount) {
      this.fieldCount = fieldCount;
    }
    public VariableFields(int fieldCount, Class fieldType) {
      this.fieldCount = fieldCount;
      this.fieldType = fieldType;
    }
    public void toData(PdxWriter writer) {
      ((PdxWriterImpl)writer).setDoExtraValidation(true);
      writer.writeInt("fieldCount", this.fieldCount);
      for (int i=0; i < this.fieldCount; i++) {
        writer.writeField("f"+i, null, this.fieldType);
      }
    }

    public void fromData(PdxReader reader) {
      throw new IllegalStateException("should never be called");
    }
    
  }
  public static class AllFieldTypes extends BasicAllFieldTypes implements
      PdxSerializable {

    public AllFieldTypes() {
      super();
    }

    public AllFieldTypes(long base, boolean nulls) {
      super(base, nulls);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.gemstone.gemfire.pdx.PdxSerializable#toData(com.gemstone.gemfire.
     * pdx.PdxWriter)
     */
    public void toData(PdxWriter out) {
      new BasicAllFieldTypesPdxSerializer().toData(this, out);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.gemstone.gemfire.pdx.PdxSerializable#fromData(com.gemstone.gemfire
     * .pdx.PdxReader)
     */
    public void fromData(PdxReader in) {
      new BasicAllFieldTypesPdxSerializer().fillInData(in, this);
    }
  }

  public static class BasicAllFieldTypesPdxSerializer implements PdxSerializer {

    public boolean toData(Object o, PdxWriter out) {
      if (o instanceof BasicAllFieldTypes) {
        BasicAllFieldTypes pdx = (BasicAllFieldTypes) o;
        // extra fields for this version
        out.writeChar("aChar", pdx.aChar);
        out.writeBoolean("aBoolean", pdx.aBoolean);
        out.writeByte("aByte", pdx.aByte);
        out.writeShort("aShort", pdx.aShort);
        out.writeInt("anInt", pdx.anInt);
        out.writeLong("aLong", pdx.aLong);
        out.writeFloat("aFloat", pdx.aFloat);
        out.writeDouble("aDouble", pdx.aDouble);
        out.writeDate("aDate", pdx.aDate);
        out.writeString("aString", pdx.aString);
        out.writeObject("anObject", pdx.anObject);
        out.writeObject("aMap", pdx.aMap);
        out.writeObject("aCollection", pdx.aCollection);
        out.writeBooleanArray("aBooleanArray", pdx.aBooleanArray);
        out.writeCharArray("aCharArray", pdx.aCharArray);
        out.writeByteArray("aByteArray", pdx.aByteArray);
        out.writeShortArray("aShortArray", pdx.aShortArray);
        out.writeIntArray("anIntArray", pdx.anIntArray);
        out.writeLongArray("aLongArray", pdx.aLongArray);
        out.writeFloatArray("aFloatArray", pdx.aFloatArray);
        out.writeDoubleArray("aDoubleArray", pdx.aDoubleArray);
        out.writeStringArray("aStringArray", pdx.aStringArray);
        out.writeObjectArray("anObjectArray", pdx.anObjectArray);
        out.writeArrayOfByteArrays("anArrayOfByteArray", pdx.anArrayOfByteArray);
        return true;
      } else {
        return false;
      }
    }

    public Object fromData(Class clazz, PdxReader in) {
      if (BasicAllFieldTypes.class.isAssignableFrom(clazz)) {
        BasicAllFieldTypes pdx = new BasicAllFieldTypes();
        fillInData(in, pdx);
        return pdx;
      } else {
        return null;
      }
    }

    public void fillInData(PdxReader in, BasicAllFieldTypes pdx) {
      // extra fields for this version
      try {
        in.readBoolean("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readByte("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readShort("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readInt("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readLong("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readFloat("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readDouble("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readDate("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readString("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readObject("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readBooleanArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readCharArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readByteArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readShortArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readIntArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readLongArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readFloatArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readDoubleArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readStringArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readObjectArray("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readArrayOfByteArrays("aChar");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      try {
        in.readChar("aBoolean");
        fail("expected PdxFieldTypeMismatchException");
      } catch (PdxFieldTypeMismatchException expected) {
        // expected
      }
      pdx.aChar = in.readChar("aChar");
      pdx.aBoolean = in.readBoolean("aBoolean");
      pdx.aByte = in.readByte("aByte");
      pdx.aShort = in.readShort("aShort");
      pdx.anInt = in.readInt("anInt");
      pdx.aLong = in.readLong("aLong");
      pdx.aFloat = in.readFloat("aFloat");
      pdx.aDouble = in.readDouble("aDouble");
      pdx.aDate = in.readDate("aDate");
      pdx.aString = in.readString("aString");
      pdx.anObject = in.readObject("anObject");
      pdx.aMap = (Map)in.readObject("aMap");
      pdx.aCollection = (Collection)in.readObject("aCollection");
      pdx.aBooleanArray = in.readBooleanArray("aBooleanArray");
      pdx.aCharArray = in.readCharArray("aCharArray");
      pdx.aByteArray = in.readByteArray("aByteArray");
      pdx.aShortArray = in.readShortArray("aShortArray");
      pdx.anIntArray = in.readIntArray("anIntArray");
      pdx.aLongArray = in.readLongArray("aLongArray");
      pdx.aFloatArray = in.readFloatArray("aFloatArray");
      pdx.aDoubleArray = in.readDoubleArray("aDoubleArray");
      pdx.aStringArray = in.readStringArray("aStringArray");
      pdx.anObjectArray = in.readObjectArray("anObjectArray");
      pdx.anArrayOfByteArray = in.readArrayOfByteArrays("anArrayOfByteArray");
    }

  }

  public static class AllFieldTypesRF extends AllFieldTypes {
    public AllFieldTypesRF(long l, boolean b) {
      super(l, b);
    }

    public AllFieldTypesRF() {
      super();
    }

    @Override
    public void fromData(PdxReader in) {
      aChar = (Character) in.readField("aChar");
      aBoolean = (Boolean) in.readField("aBoolean");
      aByte = (Byte) in.readField("aByte");
      aShort = (Short) in.readField("aShort");
      anInt = (Integer) in.readField("anInt");
      aLong = (Long) in.readField("aLong");
      aFloat = (Float) in.readField("aFloat");
      aDouble = (Double) in.readField("aDouble");
      aDate = (Date) in.readField("aDate");
      aString = (String) in.readField("aString");
      anObject = in.readField("anObject");
      aMap = (Map) in.readField("aMap");
      aCollection = (Collection) in.readField("aCollection");
      aBooleanArray = (boolean[]) in.readField("aBooleanArray");
      aCharArray = (char[]) in.readField("aCharArray");
      aByteArray = (byte[]) in.readField("aByteArray");
      aShortArray = (short[]) in.readField("aShortArray");
      anIntArray = (int[]) in.readField("anIntArray");
      aLongArray = (long[]) in.readField("aLongArray");
      aFloatArray = (float[]) in.readField("aFloatArray");
      aDoubleArray = (double[]) in.readField("aDoubleArray");
      aStringArray = (String[]) in.readField("aStringArray");
      anObjectArray = (Object[]) in.readField("anObjectArray");
      anArrayOfByteArray = (byte[][]) in.readField("anArrayOfByteArray");
    }

  }

  public static class AllFieldTypesWF extends AllFieldTypes {
    public AllFieldTypesWF(long l, boolean b) {
      super(l, b);
    }

    public AllFieldTypesWF() {
      super();
    }

    @Override
    public void toData(PdxWriter out) {
      out.writeField("aChar", this.aChar, char.class);
      out.writeField("aBoolean", this.aBoolean, boolean.class);
      out.writeField("aByte", this.aByte, byte.class);
      out.writeField("aShort", this.aShort, short.class);
      out.writeField("anInt", this.anInt, int.class);
      out.writeField("aLong", this.aLong, long.class);
      out.writeField("aFloat", this.aFloat, float.class);
      out.writeField("aDouble", this.aDouble, double.class);
      out.writeField("aDate", this.aDate, Date.class);
      out.writeField("aString", this.aString, String.class);
      out.writeField("anObject", this.anObject, Object.class);
      out.writeField("aMap", this.aMap, Map.class);
      out.writeField("aCollection", this.aCollection, Collection.class);
      out.writeField("aBooleanArray", this.aBooleanArray, boolean[].class);
      out.writeField("aCharArray", this.aCharArray, char[].class);
      out.writeField("aByteArray", this.aByteArray, byte[].class);
      out.writeField("aShortArray", this.aShortArray, short[].class);
      out.writeField("anIntArray", this.anIntArray, int[].class);
      out.writeField("aLongArray", this.aLongArray, long[].class);
      out.writeField("aFloatArray", this.aFloatArray, float[].class);
      out.writeField("aDoubleArray", this.aDoubleArray, double[].class);
      out.writeField("aStringArray", this.aStringArray, String[].class);
      out.writeField("anObjectArray", this.anObjectArray, Object[].class);
      // TODO test other types of Object[] like SimpleClass[].
      out.writeField("anArrayOfByteArray", this.anArrayOfByteArray,
                     byte[][].class);
    }
  }
  
  private byte[] createBlob(Object o) throws IOException {
    HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT);
    DataSerializer.writeObject(o, out);
    return out.toByteArray();
  }
  
  private  T deblob(byte[] blob) throws IOException, ClassNotFoundException {
    return (T)DataSerializer.readObject(
                new DataInputStream(new ByteArrayInputStream(blob)));
  }
  /**
   * Make sure that if a class adds a field that pdx serialization will
   * preserve the extra field.
   */
  public void testFieldAppend() throws Exception {
    MyEvolvablePdx.setVersion(1);
    MyEvolvablePdx pdx = new MyEvolvablePdx(7);
    assertEquals(7, pdx.f1);
    assertEquals(0, pdx.f2);
    
    MyEvolvablePdx.setVersion(2);
    pdx = new MyEvolvablePdx(7);
    assertEquals(7, pdx.f1);
    assertEquals(8, pdx.f2);
    byte[] v2actual = createBlob(pdx);
    int v2typeId = getBlobPdxTypeId(v2actual);

    c.getPdxRegistry().removeLocal(pdx);
    MyEvolvablePdx.setVersion(1);
    MyEvolvablePdx pdxv1 = deblob(v2actual);
    assertEquals(7, pdxv1.f1);
    assertEquals(0, pdxv1.f2);
    
    // now reserialize and make sure f2 is preserved
    byte[] v1actual = createBlob(pdxv1);
    assertEquals(v2typeId, getBlobPdxTypeId(v1actual));
    checkBytes(v2actual, v1actual);
    // serialize one more time to make sure internal cache works ok
    v1actual = createBlob(pdxv1);
    assertEquals(v2typeId, getBlobPdxTypeId(v1actual));
    checkBytes(v2actual, v1actual);
    // make sure CopyHelper preserves extra fields
    MyEvolvablePdx pdxv1copy = CopyHelper.copy(pdxv1);
    v1actual = createBlob(pdxv1copy);
    assertEquals(v2typeId, getBlobPdxTypeId(v1actual));
    checkBytes(v2actual, v1actual);
    
    MyEvolvablePdx.setVersion(2);
    c.getPdxRegistry().removeLocal(pdx);
    MyEvolvablePdx pdxv2 = deblob(v1actual);
    assertEquals(7, pdxv2.f1);
    assertEquals(8, pdxv2.f2);
    // make sure we can dserialize a second time with the same results
    pdxv2 = deblob(v1actual);
    assertEquals(7, pdxv2.f1);
    assertEquals(8, pdxv2.f2);
   }
  
  public void testFieldInsert() throws Exception {
    MyEvolvablePdx.setVersion(1);
    MyEvolvablePdx pdx = new MyEvolvablePdx(7);
    assertEquals(7, pdx.f1);
    assertEquals(0, pdx.f2);
    
    MyEvolvablePdx.setVersion(3);
    pdx = new MyEvolvablePdx(7);
    assertEquals(7, pdx.f1);
    assertEquals(8, pdx.f2);
    byte[] v3actual = createBlob(pdx);
    int v3typeId = getBlobPdxTypeId(v3actual);
    c.getPdxRegistry().removeLocal(pdx);
    MyEvolvablePdx.setVersion(1);
    MyEvolvablePdx pdxv1 = deblob(v3actual);
    assertEquals(7, pdxv1.f1);
    assertEquals(0, pdxv1.f2);
    
    // now reserialize and make sure f2 is preserved
    byte[] v1actual = createBlob(pdxv1);
    int mergedTypeId = getBlobPdxTypeId(v1actual);
    assertEquals(v3typeId+1, mergedTypeId);
    TypeRegistry tr = c.getPdxRegistry();
    PdxType v3Type = tr.getType(v3typeId);
    PdxType mergedType = tr.getType(mergedTypeId);
    assertFalse(mergedType.equals(v3Type));
    assertTrue(mergedType.compatible(v3Type));

    MyEvolvablePdx.setVersion(3);
    c.getPdxRegistry().removeLocal(pdxv1);
    MyEvolvablePdx pdxv3 = deblob(v1actual);
    assertEquals(7, pdxv3.f1);
    assertEquals(8, pdxv3.f2);
  }
  
  public void testFieldRemove() throws Exception {
    // this test pretends that version 1 is newer than version2
    // so it look like a field was removed.
    MyEvolvablePdx.setVersion(1);
    MyEvolvablePdx pdx = new MyEvolvablePdx(7);
    assertEquals(7, pdx.f1);
    assertEquals(0, pdx.f2);
    
    byte[] v1actual = createBlob(pdx);
    int v1typeId = getBlobPdxTypeId(v1actual);

    c.getPdxRegistry().removeLocal(pdx);
    MyEvolvablePdx.setVersion(2);
    MyEvolvablePdx pdxv2 = deblob(v1actual);
    assertEquals(7, pdxv2.f1);
    assertEquals(0, pdxv2.f2);
    pdxv2.f2 = 23;
    
    // now reserialize and make sure it is version2 and not version1
    byte[] v2actual = createBlob(pdxv2);
    int v2typeId = getBlobPdxTypeId(v2actual);
    assertEquals(v1typeId+1, v2typeId);
    
    TypeRegistry tr = c.getPdxRegistry();
    PdxType v2Type = tr.getType(v2typeId);
    PdxType v1Type = tr.getType(v1typeId);
    assertFalse(v1Type.equals(v2Type));
    assertFalse(v1Type.compatible(v2Type));
    assertNotNull(v1Type.getPdxField("f1"));
    assertNull(v1Type.getPdxField("f2"));
    assertNotNull(v2Type.getPdxField("f1"));
    assertNotNull(v2Type.getPdxField("f2"));

    MyEvolvablePdx.setVersion(1);
    c.getPdxRegistry().removeLocal(pdx);
    MyEvolvablePdx pdxv3 = deblob(v2actual);
    assertEquals(7, pdxv3.f1);
    assertEquals(0, pdxv3.f2);
  }
   
  private int getBlobPdxTypeId(byte[] blob) {
    ByteBuffer bb = ByteBuffer.wrap(blob);
    // skip byte for PDX dscode and integer for pdx length
    return bb.getInt(DataSize.BYTE_SIZE + DataSize.INTEGER_SIZE);
  }

  public static class MyEvolvablePdx implements PdxSerializable {
    private static int version = 1;
    
    private static int getVersion() {
      return version;
    }
    private static void setVersion(int v) {
      version = v;
    }
    
    public int f1;
    public int f2;
    
    public MyEvolvablePdx(int base) {
      this.f1 = base;
      base++;
      if (getVersion() >= 2) {
        this.f2 = base;
        base++;
      }
    }
    public MyEvolvablePdx() {
      // need for pdx deserialization
    }
    
    public void toData(PdxWriter writer) {
      if (getVersion() == 3) {
        writer.writeInt("f2", this.f2);
      }
       writer.writeInt("f1", this.f1);
      if (getVersion() == 2) {
        writer.writeInt("f2", this.f2);
      }
    }

    public void fromData(PdxReader reader) {
      if (getVersion() == 3) {
        this.f2 = reader.readInt("f2");
      }
      this.f1 = reader.readInt("f1");
      if (getVersion() == 2) {
        this.f2 = reader.readInt("f2");
      }
    }
  }
  

  /** PlainOldSerializable */
  public static class POS implements java.io.Serializable {
    int f;
    public POS(int i) {
      this.f = i;
    }
    public int hashCode() {
      return f;
    }
    public boolean equals(Object obj) {
      if (obj == null) return false;
      if (!(obj instanceof POS)) return false;
      POS other = (POS)obj;
      return this.f == other.f;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy