@SuppressWarnings("unchecked")
  public static <T, V> Task<V> attributePostProcessedWhenReady(
      final Entity source,
      final AttributeSensor<T> sensor,
      final Predicate<? super T> ready,
      final Function<? super T, V> postProcess) {
    Builder<T, T> builder1 = DependentConfiguration.builder().attributeWhenReady(source, sensor);
    // messy generics here to support null postProcess; would be nice to disallow that here
    Builder<T, V> builder;
    if (postProcess != null) {
      builder = builder1.postProcess(postProcess);
    } else {
      builder = (Builder<T, V>) builder1;
    }
    if (ready != null) builder.readiness(ready);

    return builder.build();
  }
    public Task<V2> build() {
      List<Task<V>> tasks = MutableList.of();
      for (AttributeAndSensorCondition<?> source : multiSource) {
        builder.source(source.source);
        builder.sensor((AttributeSensor) source.sensor);
        builder.readiness((Predicate) source.predicate);
        tasks.add(builder.build());
      }
      final Task<List<V>> parallelTask =
          Tasks.<List<V>>builder()
              .parallel(true)
              .addAll(tasks)
              .displayName(name)
              .description(
                  descriptionBase + (builder.timeout != null ? ", timeout " + builder.timeout : ""))
              .build();

      if (postProcessFromMultiple == null) {
        // V2 should be the right type in normal operations
        return (Task<V2>) parallelTask;
      } else {
        return Tasks.<V2>builder()
            .displayName(name)
            .description(descriptionBase)
            .tag("attributeWhenReady")
            .body(
                new Callable<V2>() {
                  @Override
                  public V2 call() throws Exception {
                    List<V> prePostProgress = DynamicTasks.queue(parallelTask).get();
                    return DynamicTasks.queue(
                            Tasks.<V2>builder()
                                .displayName("post-processing")
                                .description("Applying " + postProcessFromMultiple)
                                .body(
                                    Functionals.callable(postProcessFromMultiple, prePostProgress))
                                .build())
                        .get();
                  }
                })
            .build();
      }
    }
    @Beta
    protected MultiBuilder(
        Iterable<? extends Entity> sources,
        AttributeSensor<T> sensor,
        Predicate<? super T> readiness) {
      builder = new Builder<T, V>(null, sensor);
      builder.readiness(readiness);

      for (Entity s : checkNotNull(sources, "sources")) {
        multiSource.add(new AttributeAndSensorCondition<T>(s, sensor, readiness));
      }
      this.name = "waiting on " + sensor.getName();
      this.descriptionBase =
          "waiting on "
              + sensor.getName()
              + " "
              + readiness
              + " from "
              + Iterables.size(sources)
              + " entit"
              + Strings.ies(sources);
    }
 /**
  * returns an unsubmitted {@link Task} which blocks until the given sensor on the given source
  * entity gives a value that satisfies ready, then returns that value; particular useful in Entity
  * configuration where config will block until Tasks have a value
  */
 public static <T> Task<T> attributeWhenReady(
     final Entity source, final AttributeSensor<T> sensor, final Predicate<? super T> ready) {
   Builder<T, T> builder = builder().attributeWhenReady(source, sensor);
   if (ready != null) builder.readiness(ready);
   return builder.build();
 }