class ExtraFactory {

  public final TraceContext decorate(TraceContext context) {
    long traceId = context.traceId(), spanId = context.spanId();

    E claimed = null;
    int existingIndex = -1, extraLength = context.extra().size();
    for (int i = 0; i < extraLength; i++) {
      Object next = context.extra().get(i);
      if (next instanceof Extra) {
        Extra nextExtra = (Extra) next;
        // Don't interfere with other instances or subtypes
        if (nextExtra.factory != this) continue;

        if (claimed == null && nextExtra.tryToClaim(traceId, spanId)) {
          claimed = (E) nextExtra;
          continue;
        }

        if (existingIndex == -1) {
          existingIndex = i;
        } else {
          Platform.get().log("BUG: something added redundant extra instances %s", context, null);
          return context;
        }
      }
    }

    // Easiest when there is neither existing state to assign, nor need to change context.extra()
    if (claimed != null && existingIndex == -1) {
      return context;
    }

    // If context.extra() didn't have an unclaimed extra instance, create one for this context.
    if (claimed == null) {
      claimed = create();
      if (claimed == null) {
        Platform.get().log("BUG: create() returned null", null);
        return context;
      }
      claimed.tryToClaim(traceId, spanId);
    }

    TraceContext.Builder builder = context.toBuilder().clearExtra().addExtra(claimed);

    for (int i = 0; i < extraLength; i++) {
      Object next = context.extra().get(i);
      if (i == existingIndex) {
        E existing = (E) next;
        // If the claimed extra instance was new or had no changes, simply assign existing to it
        if (claimed.state == initialState) {
          claimed.state = existing.state;
        } else if (existing.state != initialState) {
          claimed.mergeStateKeepingOursOnConflict(existing);
        }
      } else if (!next.equals(claimed)) {
        builder.addExtra(next);
      }
    }

    return builder.build();
  }

  protected  ExtraFactory(Object initialState);

}