protected <O> O instantiate(int id, DataContainer container, ClassTypeModel<O> typeModel)
      throws IOException {
    Map<FieldModel<? super O, ?>, Object> state = new HashMap<FieldModel<? super O, ?>, Object>();
    ClassTypeModel<? super O> currentTypeModel = typeModel;
    List<FieldUpdate<O>> sets = new ArrayList<FieldUpdate<O>>();
    while (currentTypeModel != null) {
      if (currentTypeModel instanceof ClassTypeModel) {
        for (FieldModel<? super O, ?> fieldModel : currentTypeModel.getFields()) {
          if (!fieldModel.isTransient()) {
            switch (container.readInt()) {
              case DataKind.NULL_VALUE:
                state.put(fieldModel, null);
                break;
              case DataKind.OBJECT_REF:
                int refId = container.readInt();
                Object refO = idToObject.get(refId);
                if (refO != null) {
                  state.put(fieldModel, refO);
                } else {
                  sets.add(new FieldUpdate<O>(refId, fieldModel));
                }
                break;
              case DataKind.OBJECT:
                Object o = container.readObject();
                state.put(fieldModel, o);
                break;
            }
          }
        }
      }
      currentTypeModel = currentTypeModel.getSuperType();
    }

    //
    O instance = instantiate(typeModel, state);

    // Create future field updates
    for (FieldUpdate<O> set : sets) {
      List<FutureFieldUpdate<?>> resolutions = idToResolutions.get(set.ref);
      if (resolutions == null) {
        resolutions = new ArrayList<FutureFieldUpdate<?>>();
        idToResolutions.put(set.ref, resolutions);
      }
      resolutions.add(new FutureFieldUpdate<O>(instance, set.fieldModel));
    }

    //
    idToObject.put(id, instance);

    // Resolve future field updates
    List<FutureFieldUpdate<?>> resolutions = idToResolutions.remove(id);
    if (resolutions != null) {
      for (FutureFieldUpdate<?> resolution : resolutions) {
        resolution.fieldModel.castAndSet(resolution.target, instance);
      }
    }

    //
    return instance;
  }
 private Object read(DataContainer container) throws IOException {
   int sw = container.readInt();
   switch (sw) {
     case DataKind.OBJECT_REF:
       {
         int id = container.readInt();
         Object o1 = idToObject.get(id);
         if (o1 == null) {
           throw new AssertionError();
         }
         return o1;
       }
     case DataKind.OBJECT:
       {
         int id = container.readInt();
         Class<?> clazz = (Class) container.readObject();
         ClassTypeModel<?> typeModel =
             (ClassTypeModel<?>) context.getTypeDomain().getTypeModel(clazz);
         return instantiate(id, container, typeModel);
       }
     case DataKind.CONVERTED_OBJECT:
       {
         Class<?> tclazz = (Class<?>) container.readObject();
         ConvertedTypeModel<?, ?> ctm =
             (ConvertedTypeModel<?, ?>) context.getTypeDomain().getTypeModel(tclazz);
         return convertObject(container, ctm);
       }
     case DataKind.SERIALIZED_OBJECT:
       return container.readObject();
     default:
       throw new StreamCorruptedException("Unrecognized data " + sw);
   }
 }