@Test
  public void testIndexWithNull() throws QueryException {
    Indexes is = new Indexes();
    Index strIndex = is.addOrGetIndex("str", true);

    Data value = ss.toData(new MainPortable(false, 1, null));
    Data key1 = ss.toData(0);
    is.saveEntryIndex(new QueryEntry(ss, key1, key1, value));

    value = ss.toData(new MainPortable(false, 2, null));
    Data key2 = ss.toData(1);
    is.saveEntryIndex(new QueryEntry(ss, key2, key2, value));

    for (int i = 2; i < 1000; i++) {
      Data key = ss.toData(i);
      value = ss.toData(new MainPortable(false, 1 * (i + 1), "joe" + i));
      is.saveEntryIndex(new QueryEntry(ss, key, key, value));
    }

    Comparable c = null;
    assertEquals(2, strIndex.getRecords(c).size());
    assertEquals(998, strIndex.getSubRecords(ComparisonType.NOT_EQUAL, null).size());
  }
 @Test
 public void testRemoveEnumIndex() {
   Indexes is = new Indexes();
   is.addOrGetIndex("favoriteCity", false);
   Data key = ss.toData(1);
   Data value = ss.toData(new SerializableWithEnum(SerializableWithEnum.City.Istanbul));
   is.saveEntryIndex(new QueryEntry(ss, key, key, value));
   assertNotNull(is.getIndex("favoriteCity"));
   is.removeEntryIndex(key);
   assertEquals(
       0, is.getIndex("favoriteCity").getRecords(SerializableWithEnum.City.Istanbul).size());
 }
  @SuppressWarnings("rawtypes")
  public void updateFromData(
      Object obj, ByteBuffer byteBuffer, float[] floatValues, String[] stringValues, Indexes index)
      throws IllegalArgumentException, IllegalAccessException {

    Reporter r = null;

    if (BuildCraftCore.trackNetworkUsage) {
      if (!report.containsKey(clas.getName())) report.put(clas.getName(), new Reporter());

      r = report.get(clas.getName());
      r.clas = clas;
    } else r = new Reporter();

    r.occurences++;

    for (Field f : shortFields) {
      f.setShort(obj, byteBuffer.readShort());
      r.bytes += 2;
      r.dataInt += 1;
    }

    for (Field f : intFields) {
      f.setInt(obj, byteBuffer.readInt());
      r.bytes += 4;
      r.dataInt += 1;
    }

    for (Field f : booleanFields) {
      f.setBoolean(obj, byteBuffer.readUnsignedByte() == 1);
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : enumFields) {
      f.set(obj, ((Class) f.getGenericType()).getEnumConstants()[byteBuffer.readUnsignedByte()]);
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : unsignedByteFields) {
      f.setInt(obj, byteBuffer.readUnsignedByte());
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : floatFields) {
      f.setFloat(obj, floatValues[index.floatIndex]);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : doubleFields) {
      f.setDouble(obj, floatValues[index.floatIndex]);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : stringFields) {
      f.set(obj, stringValues[index.stringIndex]);
      r.bytes += stringValues[index.stringIndex].length();
      index.stringIndex++;
      r.dataString += 1;
    }

    for (ClassMapping c : objectFields) {
      boolean isNull = byteBuffer.readUnsignedByte() == 0;
      r.bytes += 1;
      r.dataInt += 1;

      if (isNull) {
        for (int i = 0; i < c.sizeBytes; ++i) byteBuffer.readUnsignedByte();

        index.floatIndex += c.sizeFloat;
        index.stringIndex += c.sizeString;
      } else c.updateFromData(c.field.get(obj), byteBuffer, floatValues, stringValues, index);
    }

    for (Field f : doubleArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((double[]) f.get(obj))[i] = floatValues[index.floatIndex];
        index.floatIndex++;
        r.bytes += 4;
        r.dataFloat += 1;
      }
    }

    for (Field f : shortArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((short[]) f.get(obj))[i] = byteBuffer.readShort();
        r.bytes += 2;
        r.dataInt += 1;
      }
    }

    for (Field f : intArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((int[]) f.get(obj))[i] = byteBuffer.readInt();
        r.bytes += 4;
        r.dataInt += 1;
      }
    }

    for (Field f : booleanArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((boolean[]) f.get(obj))[i] = byteBuffer.readUnsignedByte() == 1;
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : unsignedByteArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        ((int[]) f.get(obj))[i] = byteBuffer.readUnsignedByte();
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : stringArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      String[] strs = (String[]) f.get(obj);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        strs[i] = stringValues[index.stringIndex];
        r.bytes += stringValues[index.stringIndex].length();
        index.stringIndex++;
        r.dataString += 1;
      }
    }

    for (ClassMapping c : objectArrayFields) {
      TileNetworkData updateAnnotation = c.field.getAnnotation(TileNetworkData.class);

      Object[] cpts = (Object[]) c.field.get(obj);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        boolean isNull = byteBuffer.readUnsignedByte() == 0;
        r.bytes += 1;
        r.dataInt += 1;

        if (isNull) {
          for (int j = 0; j < c.sizeBytes; ++j) byteBuffer.readUnsignedByte();

          index.floatIndex += c.sizeFloat;
          index.stringIndex += c.sizeString;
        } else c.updateFromData(cpts[i], byteBuffer, floatValues, stringValues, index);
      }
    }
  }
  @SuppressWarnings("rawtypes")
  public void setData(
      Object obj, ByteBuffer byteBuffer, float[] floatValues, String[] stringValues, Indexes index)
      throws IllegalArgumentException, IllegalAccessException {

    Reporter r = null;

    if (BuildCraftCore.trackNetworkUsage) {
      if (!report.containsKey(clas.getName())) report.put(clas.getName(), new Reporter());

      r = report.get(clas.getName());
      r.clas = clas;
    } else r = new Reporter();

    r.occurences++;

    for (Field f : shortFields) {
      byteBuffer.writeShort(f.getShort(obj));
      r.bytes += 2;
      r.dataInt += 1;
    }

    for (Field f : intFields) {
      byteBuffer.writeInt(f.getInt(obj));
      r.bytes += 4;
      r.dataInt += 1;
    }

    for (Field f : booleanFields) {
      byteBuffer.writeUnsignedByte(f.getBoolean(obj) ? 1 : 0);
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : enumFields) {
      byteBuffer.writeUnsignedByte(((Enum) f.get(obj)).ordinal());
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : unsignedByteFields) {
      byteBuffer.writeUnsignedByte(f.getInt(obj));
      r.bytes += 1;
      r.dataInt += 1;
    }

    for (Field f : floatFields) {
      floatValues[index.floatIndex] = f.getFloat(obj);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : doubleFields) {
      floatValues[index.floatIndex] = (float) f.getDouble(obj);
      index.floatIndex++;
      r.bytes += 4;
      r.dataFloat += 1;
    }

    for (Field f : stringFields) {
      stringValues[index.stringIndex] = (String) f.get(obj);
      r.bytes += stringValues[index.stringIndex].length();
      index.stringIndex++;
      r.dataString += 1;
    }

    for (ClassMapping c : objectFields) {
      Object cpt = c.field.get(obj);

      if (cpt == null) {
        byteBuffer.writeUnsignedByte(0);

        for (int i = 0; i < c.sizeBytes; ++i) {
          byteBuffer.writeUnsignedByte(0);
          r.bytes += 1;
          r.dataInt += 1;
        }

        index.floatIndex += c.sizeFloat;
        index.stringIndex += c.sizeString;
      } else {
        byteBuffer.writeUnsignedByte(1);
        r.bytes += 1;
        r.dataInt += 1;

        c.setData(cpt, byteBuffer, floatValues, stringValues, index);
      }
    }

    for (Field f : doubleArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        floatValues[index.floatIndex] = (float) ((double[]) f.get(obj))[i];
        index.floatIndex++;
        r.bytes += 4;
        r.dataFloat += 1;
      }
    }

    for (Field f : shortArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeShort(((short[]) f.get(obj))[i]);
        r.bytes += 2;
        r.dataInt += 1;
      }
    }

    for (Field f : intArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeInt(((int[]) f.get(obj))[i]);
        r.bytes += 4;
        r.dataInt += 1;
      }
    }

    for (Field f : booleanArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeUnsignedByte(((boolean[]) f.get(obj))[i] ? 1 : 0);
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : unsignedByteFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        byteBuffer.writeUnsignedByte(((int[]) f.get(obj))[i]);
        r.bytes += 1;
        r.dataInt += 1;
      }
    }

    for (Field f : stringArrayFields) {
      TileNetworkData updateAnnotation = f.getAnnotation(TileNetworkData.class);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i) {
        stringValues[index.stringIndex] = ((String[]) f.get(obj))[i];
        r.bytes += stringValues[index.stringIndex].length();
        index.stringIndex++;
        r.dataString += 1;
      }
    }

    for (ClassMapping c : objectArrayFields) {
      TileNetworkData updateAnnotation = c.field.getAnnotation(TileNetworkData.class);

      Object[] cpts = (Object[]) c.field.get(obj);

      for (int i = 0; i < updateAnnotation.staticSize(); ++i)
        if (cpts[i] == null) {
          byteBuffer.writeUnsignedByte(0);

          for (int j = 0; j < c.sizeBytes; ++j) {
            byteBuffer.writeUnsignedByte(0);
            r.bytes += 1;
            r.dataInt += 1;
          }

          index.floatIndex += c.sizeFloat;
          index.stringIndex += c.sizeString;
        } else {
          byteBuffer.writeUnsignedByte(1);
          r.bytes += 1;
          r.dataInt += 1;

          c.setData(cpts[i], byteBuffer, floatValues, stringValues, index);
        }
    }
  }
 @Test
 public void testIndex() throws QueryException {
   Indexes is = new Indexes();
   Index dIndex = is.addOrGetIndex("d", false);
   Index boolIndex = is.addOrGetIndex("bool", false);
   Index strIndex = is.addOrGetIndex("str", false);
   for (int i = 0; i < 1000; i++) {
     Data key = ss.toData(i);
     Data value = ss.toData(new MainPortable(i % 2 == 0, -10.34d, "joe" + i));
     is.saveEntryIndex(new QueryEntry(ss, key, key, value));
   }
   assertEquals(1000, dIndex.getRecords(-10.34d).size());
   assertEquals(1, strIndex.getRecords("joe23").size());
   assertEquals(500, boolIndex.getRecords(true).size());
   for (int i = 0; i < 1000; i++) {
     Data key = ss.toData(i);
     Data value = ss.toData(new MainPortable(false, 11.34d, "joe"));
     is.saveEntryIndex(new QueryEntry(ss, key, key, value));
   }
   assertEquals(0, dIndex.getRecords(-10.34d).size());
   assertEquals(0, strIndex.getRecords("joe23").size());
   assertEquals(1000, strIndex.getRecords("joe").size());
   assertEquals(1000, boolIndex.getRecords(false).size());
   assertEquals(0, boolIndex.getRecords(true).size());
   for (int i = 0; i < 1000; i++) {
     Data key = ss.toData(i);
     Data value = ss.toData(new MainPortable(false, -1 * (i + 1), "joe" + i));
     is.saveEntryIndex(new QueryEntry(ss, key, key, value));
   }
   assertEquals(0, dIndex.getSubRecordsBetween(1d, 1001d).size());
   assertEquals(1000, dIndex.getSubRecordsBetween(-1d, -1001d).size());
   for (int i = 0; i < 1000; i++) {
     Data key = ss.toData(i);
     Data value = ss.toData(new MainPortable(false, 1 * (i + 1), "joe" + i));
     is.saveEntryIndex(new QueryEntry(ss, key, key, value));
   }
   assertEquals(1000, dIndex.getSubRecordsBetween(1d, 1001d).size());
   assertEquals(0, dIndex.getSubRecordsBetween(-1d, -1001d).size());
   assertEquals(400, dIndex.getSubRecords(ComparisonType.GREATER, 600d).size());
   assertEquals(401, dIndex.getSubRecords(ComparisonType.GREATER_EQUAL, 600d).size());
   assertEquals(9, dIndex.getSubRecords(ComparisonType.LESSER, 10d).size());
   assertEquals(10, dIndex.getSubRecords(ComparisonType.LESSER_EQUAL, 10d).size());
   assertEquals(
       1,
       is.query(new AndPredicate(new EqualPredicate("d", 1d), new EqualPredicate("bool", "false")))
           .size());
   assertEquals(
       1,
       is.query(
               new AndPredicate(
                   new EqualPredicate("d", 1), new EqualPredicate("bool", Boolean.FALSE)))
           .size());
   assertEquals(
       1,
       is.query(new AndPredicate(new EqualPredicate("d", "1"), new EqualPredicate("bool", false)))
           .size());
 }