<commit_before>package org.stevedowning.remo.internal.common.future;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.stevedowning.remo.Callback;
import org.stevedowning.remo.Future;
import org.stevedowning.remo.Result;
import org.stevedowning.remo.ThrowingFunction;

public class TransformedFuture<T, U> implements Future<U> {
    private final Future<T> backingFuture;
    private final ThrowingFunction<T, U> transformFunction;
    private volatile Result<U> transformedResult = null;
    private volatile boolean isTransformationError = false;

    public TransformedFuture(Future<T> future, ThrowingFunction<T, U> transformFunction) {
        this.backingFuture = future;
        this.transformFunction = transformFunction;
        
        // Eagerly transform the result.
        future.addCallback((Result<T> result) -> cacheTransformedResult());
    }
    
    private U transformVal(T val) throws ExecutionException {
        if (val == null) return null;
        try {
            return transformFunction.apply(val);
        } catch (Exception ex) {
            isTransformationError = true;
            throw new ExecutionException(ex);
        }
    }
    
    private synchronized void cacheTransformedResult() {
        if (transformedResult != null) return;
        // TODO: Use a PresetFuture.
        BasicFuture<U> transformedResult = new BasicFuture<U>();
        try {
            T val = backingFuture.get();
            U transformedVal = transformVal(val);
            transformedResult.setVal(transformedVal);
        } catch (ExecutionException ex) {
            transformedResult.setException(ex);
        } catch (InterruptedException ex) {
            transformedResult.setException(ex);
        } catch (IOException ex) {
            transformedResult.setException(ex);
        }
        this.transformedResult = transformedResult;
    }

    public U get() throws InterruptedException, ExecutionException, IOException {
        backingFuture.get();
        cacheTransformedResult();
        return transformedResult.get();
    }

    public U get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, IOException {
        backingFuture.get(timeout, unit);
        cacheTransformedResult();
        return transformedResult.get();
    }

    public boolean isDone() {
        return isSuccess() || isError() || isCancelled();
    }
    public boolean isCancelled() { return backingFuture.isCancelled(); }
    public boolean isError() {
        return isTransformationError() || backingFuture.isError();
    }
    public boolean isSuccess() {
        return backingFuture.isSuccess() && transformedResult != null && !isTransformationError();
    }
    private boolean isTransformationError() {
        return isTransformationError;
    }

    public Future<U> addCallback(Callback<U> callback) {
        backingFuture.addCallback((Result<T> result) -> {
            cacheTransformedResult();
            callback.handleResult(transformedResult);
        });
        return this;
    }
<commit_msg>isDone() correction<commit_after>package org.stevedowning.remo.internal.common.future;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.stevedowning.remo.Callback;
import org.stevedowning.remo.Future;
import org.stevedowning.remo.Result;
import org.stevedowning.remo.ThrowingFunction;

public class TransformedFuture<T, U> implements Future<U> {
    private final Future<T> backingFuture;
    private final ThrowingFunction<T, U> transformFunction;
    private volatile Result<U> transformedResult = null;
    private volatile boolean isTransformationError = false;

    public TransformedFuture(Future<T> future, ThrowingFunction<T, U> transformFunction) {
        this.backingFuture = future;
        this.transformFunction = transformFunction;
        
        // Eagerly transform the result.
        future.addCallback((Result<T> result) -> cacheTransformedResult());
    }
    
    private U transformVal(T val) throws ExecutionException {
        if (val == null) return null;
        try {
            return transformFunction.apply(val);
        } catch (Exception ex) {
            isTransformationError = true;
            throw new ExecutionException(ex);
        }
    }
    
    private synchronized void cacheTransformedResult() {
        if (transformedResult != null) return;
        // TODO: Use a PresetFuture.
        BasicFuture<U> transformedResult = new BasicFuture<U>();
        try {
            T val = backingFuture.get();
            U transformedVal = transformVal(val);
            transformedResult.setVal(transformedVal);
        } catch (ExecutionException ex) {
            transformedResult.setException(ex);
        } catch (InterruptedException ex) {
            transformedResult.setException(ex);
        } catch (IOException ex) {
            transformedResult.setException(ex);
        }
        this.transformedResult = transformedResult;
    }

    public U get() throws InterruptedException, ExecutionException, IOException {
        backingFuture.get();
        cacheTransformedResult();
        return transformedResult.get();
    }

    public U get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, IOException {
        backingFuture.get(timeout, unit);
        cacheTransformedResult();
        return transformedResult.get();
    }

    public boolean isDone() {
        return backingFuture.isDone() && transformedResult != null;
    }
    public boolean isCancelled() { return backingFuture.isCancelled(); }
    public boolean isError() {
        return isTransformationError() || backingFuture.isError();
    }
    public boolean isSuccess() {
        return backingFuture.isSuccess() && transformedResult != null && !isTransformationError();
    }
    private boolean isTransformationError() {
        return isTransformationError;
    }

    public Future<U> addCallback(Callback<U> callback) {
        backingFuture.addCallback((Result<T> result) -> {
            cacheTransformedResult();
            callback.handleResult(transformedResult);
        });
        return this;
    }

{'fraction_non_alphanumeric': 0.09317460317460317, 'fraction_numerical': 0.0, 'mean_word_length': 2.901547987616099, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2409995', 'n_tokens_mistral': 1605, 'n_tokens_neox': 1594, 'n_words': 371}