How to return the first exception that occurred?

Viewed 980

There's a method called validate which as input accepts an instance of Option and a Predicate - two arguments (yeah, I know Option should be passed as an argument but this is simplified real-world scenario here. Now, if Option is empty I need to throw NotFoundException. When it has a value that doesn't match the Predicate passed it should fail with ForbiddenException. If it has value and it matches the predicate nothing happens.

So this will be:

                Option, Predicate
                     /\
          isEmpty() /  \ isDefined()  
                   /    \
            throw NFE    matches(Predicate)
                        / \
                    no /   \ yes 
                      /     \
                throw FE     end

I have some solutions but would like potential answerers to come to this question with clear mind ;) My problem is getting the first exception if both tests fail. I expect an elegant solution if I may expect anything ;) All vavr beings allowed (Either, Validation, Try..)

One of the ideas is to use double Option:

Option
  .of(o.getOrElseThrow(NotFoundException::new))
  .filter(p)
  .getOrElseThrow(ForbiddenException::new);

But this seems a bit awkward.

Here sample project may be found.

2 Answers

As I see it, the best option would be to create readable code, which in this case means using two if statements:

if (!o.isPresent()) throw new NotFoundException();
if (!p.test(o.get())) throw new ForbiddenException();

Simple, readable and straightforward for whoever will need to maintain this code in the future.

If nonetheless you want to do it in some functionalish style:

o.orElseThrow(NotFoundException::new);
o.filter(p).orElseThrow(ForbiddenException::new);

With Java 9:

o.ifPresentOrElse(
    t -> if (!p.test(t)) throw new ForbiddenException(), 
    () -> throw new NotFoundException());

This would be the solution with Optional (don't have the time to check vavr but should be close enough)

public static <T> void validate( Optional<T> optional, Predicate<T> pred ) throws NotFoundException, ForbiddenException {
    if ( !pred.test( optional.orElseThrow( NotFoundException::new ) ) )
        throw new ForbiddenException();
}

Don't know a prettier way to throw that ForbiddenException unfortunately.

If you want a full functional solution, you will need an Optional

Optional.of( 
    pred.test( 
       option.orElseThrow( NotFoundException::new ) 
    ) 
).filter( b -> b ).orElseThrow( ForbiddenException::new );

The exception are thrown in the correct order since we only check the Predicate after the option check.

Related