private static void writeCheckedCollectionTo(
      Output output, Object value, Schema<?> currentSchema, IdStrategy strategy, int id)
      throws IOException {
    final Object c, type;
    try {
      c = fCheckedCollection_c.get(value);
      type = fCheckedCollection_type.get(value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    output.writeObject(id, c, strategy.POLYMORPHIC_COLLECTION_SCHEMA, false);
    output.writeObject(1, type, strategy.CLASS_SCHEMA, false);
  }
  private static void writeUnmodifiableCollectionTo(
      Output output, Object value, Schema<?> currentSchema, IdStrategy strategy, int id)
      throws IOException {
    final Object c;
    try {
      c = fUnmodifiableCollection_c.get(value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    output.writeObject(id, c, strategy.POLYMORPHIC_COLLECTION_SCHEMA, false);
  }
  private static void writeSynchronizedCollectionTo(
      Output output, Object value, Schema<?> currentSchema, IdStrategy strategy, int id)
      throws IOException {
    final Object c, mutex;
    try {
      c = fSynchronizedCollection_c.get(value);
      mutex = fSynchronizedCollection_mutex.get(value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    if (mutex != value) {
      // TODO for future release, introduce an interface(GraphOutput) so
      // we
      // can check whether the output can retain references.
      throw new RuntimeException(
          "This exception is thrown to fail fast. "
              + "Synchronized collections with a different mutex would only "
              + "work if graph format is used, since the reference is retained.");
    }

    output.writeObject(id, c, strategy.POLYMORPHIC_COLLECTION_SCHEMA, false);
  }
  static void writeNonPublicCollectionTo(
      Output output, Object value, Schema<?> currentSchema, IdStrategy strategy)
      throws IOException {
    final Integer num = __nonPublicCollections.get(value.getClass());
    if (num == null) throw new RuntimeException("Unknown collection: " + value.getClass());
    final int id = num.intValue();
    switch (id) {
      case ID_EMPTY_SET:
        output.writeUInt32(id, 0, false);
        break;

      case ID_EMPTY_LIST:
        output.writeUInt32(id, 0, false);
        break;

      case ID_SINGLETON_SET:
        {
          output.writeUInt32(id, 0, false);

          final Object element;
          try {
            element = fSingletonSet_element.get(value);
          } catch (Exception e) {
            throw new RuntimeException(e);
          }

          if (element != null) output.writeObject(1, element, strategy.OBJECT_SCHEMA, false);

          break;
        }

      case ID_SINGLETON_LIST:
        {
          output.writeUInt32(id, 0, false);

          // faster path (reflection not needed to get the single element).
          final Object element = ((List<?>) value).get(0);

          if (element != null) output.writeObject(1, element, strategy.OBJECT_SCHEMA, false);

          break;
        }

      case ID_SET_FROM_MAP:
        {
          final Object m;
          try {
            m = fSetFromMap_m.get(value);
          } catch (Exception e) {
            throw new RuntimeException(e);
          }

          output.writeObject(id, m, strategy.POLYMORPHIC_MAP_SCHEMA, false);

          break;
        }

      case ID_COPIES_LIST:
        {
          output.writeUInt32(id, 0, false);

          final int n = ((List<?>) value).size();
          final Object element;
          try {
            element = fCopiesList_element.get(value);
          } catch (Exception e) {
            throw new RuntimeException(e);
          }

          output.writeUInt32(1, n, false);

          if (element != null) output.writeObject(2, element, strategy.OBJECT_SCHEMA, false);

          break;
        }
      case ID_UNMODIFIABLE_COLLECTION:
        writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_UNMODIFIABLE_SET:
        writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_UNMODIFIABLE_SORTED_SET:
        writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_UNMODIFIABLE_LIST:
        writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_UNMODIFIABLE_RANDOM_ACCESS_LIST:
        writeUnmodifiableCollectionTo(output, value, currentSchema, strategy, id);
        break;

      case ID_SYNCHRONIZED_COLLECTION:
        writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_SYNCHRONIZED_SET:
        writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_SYNCHRONIZED_SORTED_SET:
        writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_SYNCHRONIZED_LIST:
        writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_SYNCHRONIZED_RANDOM_ACCESS_LIST:
        writeSynchronizedCollectionTo(output, value, currentSchema, strategy, id);
        break;

      case ID_CHECKED_COLLECTION:
        writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_CHECKED_SET:
        writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_CHECKED_SORTED_SET:
        writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_CHECKED_LIST:
        writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
        break;
      case ID_CHECKED_RANDOM_ACCESS_LIST:
        writeCheckedCollectionTo(output, value, currentSchema, strategy, id);
        break;

      default:
        throw new RuntimeException("Should not happen.");
    }
  }
  static void transferObject(
      Pipe.Schema<Object> pipeSchema,
      Pipe pipe,
      Input input,
      Output output,
      IdStrategy strategy,
      final int number)
      throws IOException {
    switch (number) {
      case ID_EMPTY_SET:
        output.writeUInt32(number, input.readUInt32(), false);
        break;

      case ID_EMPTY_LIST:
        output.writeUInt32(number, input.readUInt32(), false);
        break;

      case ID_SINGLETON_SET:
      case ID_SINGLETON_LIST:
        {
          output.writeUInt32(number, input.readUInt32(), false);

          final int next = input.readFieldNumber(pipeSchema.wrappedSchema);
          if (next == 0) {
            // null element
            return;
          }

          if (next != 1) throw new ProtostuffException("Corrupt input.");

          output.writeObject(1, pipe, strategy.OBJECT_PIPE_SCHEMA, false);
          break;
        }
      case ID_SET_FROM_MAP:
        output.writeObject(number, pipe, strategy.POLYMORPHIC_MAP_PIPE_SCHEMA, false);
        break;

      case ID_COPIES_LIST:
        {
          output.writeUInt32(number, input.readUInt32(), false);

          if (1 != input.readFieldNumber(pipeSchema.wrappedSchema))
            throw new ProtostuffException("Corrupt input.");

          // size
          output.writeUInt32(1, input.readUInt32(), false);

          final int next = input.readFieldNumber(pipeSchema.wrappedSchema);
          if (next == 0) {
            // null element
            return;
          }

          if (next != 2) throw new ProtostuffException("Corrupt input.");

          output.writeObject(2, pipe, strategy.OBJECT_PIPE_SCHEMA, false);
          break;
        }
      case ID_UNMODIFIABLE_COLLECTION:
      case ID_UNMODIFIABLE_SET:
      case ID_UNMODIFIABLE_SORTED_SET:
      case ID_UNMODIFIABLE_LIST:
      case ID_UNMODIFIABLE_RANDOM_ACCESS_LIST:
        output.writeObject(number, pipe, strategy.POLYMORPHIC_COLLECTION_PIPE_SCHEMA, false);
        break;

      case ID_SYNCHRONIZED_COLLECTION:
      case ID_SYNCHRONIZED_SET:
      case ID_SYNCHRONIZED_SORTED_SET:
      case ID_SYNCHRONIZED_LIST:
      case ID_SYNCHRONIZED_RANDOM_ACCESS_LIST:
        output.writeObject(number, pipe, strategy.POLYMORPHIC_COLLECTION_PIPE_SCHEMA, false);
        break;

      case ID_CHECKED_COLLECTION:
      case ID_CHECKED_SET:
      case ID_CHECKED_SORTED_SET:
      case ID_CHECKED_LIST:
      case ID_CHECKED_RANDOM_ACCESS_LIST:
        output.writeObject(number, pipe, strategy.POLYMORPHIC_COLLECTION_PIPE_SCHEMA, false);

        if (1 != input.readFieldNumber(pipeSchema.wrappedSchema))
          throw new ProtostuffException("Corrupt input.");

        output.writeObject(1, pipe, strategy.CLASS_PIPE_SCHEMA, false);
        break;

      case ID_ENUM_SET:
        strategy.transferEnumId(input, output, number);

        if (output instanceof StatefulOutput) {
          // update using the derived schema.
          ((StatefulOutput) output).updateLast(strategy.COLLECTION_PIPE_SCHEMA, pipeSchema);
        }

        // TODO use enum schema
        Pipe.transferDirect(strategy.COLLECTION_PIPE_SCHEMA, pipe, input, output);
        return;
      case ID_COLLECTION:
        strategy.transferCollectionId(input, output, number);

        if (output instanceof StatefulOutput) {
          // update using the derived schema.
          ((StatefulOutput) output).updateLast(strategy.COLLECTION_PIPE_SCHEMA, pipeSchema);
        }

        Pipe.transferDirect(strategy.COLLECTION_PIPE_SCHEMA, pipe, input, output);
        return;
      default:
        throw new ProtostuffException("Corrupt input.");
    }

    if (0 != input.readFieldNumber(pipeSchema.wrappedSchema))
      throw new ProtostuffException("Corrupt input.");
  }