/*
 * Copyright 2019 Immutables Authors and Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.immutables.criteria.inmemory;

import org.immutables.criteria.expression.Path;
import org.immutables.criteria.reflect.MemberExtractor;

import javax.annotation.Nullable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.util.Objects;
import java.util.Optional;

class ReflectionExtractor implements PathExtractor {

  private final MemberExtractor extractor;

  ReflectionExtractor() {
    this.extractor = MemberExtractor.ofReflection();
  }

  @Override
  @Nullable
  public Object extract(Path path, Object instance) {
    Objects.requireNonNull(path, "path");

    Object result = instance;

    for (AnnotatedElement member: path.paths()) {
      result = extract((Member) member, result);
      result = maybeUnwrapOptional(result);
      if (result == null) {
        return null;
      }
    }

    return result;
  }

  private Object extract(Member member, Object instance) {
    if (instance == null) {
      return null;
    }

    return extractor.extract(member, instance);
  }

  @SuppressWarnings("unchecked")
  private static Object maybeUnwrapOptional(Object maybeOptional) {
    if ((maybeOptional instanceof Optional)) {
      return  ((Optional) maybeOptional).orElse(null);
    }

    if (maybeOptional instanceof com.google.common.base.Optional) {
      return ((com.google.common.base.Optional) maybeOptional).orNull();
    }

    return maybeOptional;
  }
}

@@ -1067,16 +1067,48 @@
 actor = 
+new UnwrapOptional(new NullSafe(
 MemberEx
@@ -1129,16 +1129,18 @@
 ection()
+))
 ;%0A  %7D%0A%0A 
@@ -1355,16 +1355,26 @@
 esult = 
+extractor.
 extract(
@@ -1403,52 +1403,8 @@
 t);%0A
-      result = maybeUnwrapOptional(result);%0A
     
@@ -1497,16 +1497,225 @@
 private 
+static class NullSafe implements MemberExtractor %7B%0A    private final MemberExtractor delegate;%0A%0A    private NullSafe(MemberExtractor delegate) %7B%0A      this.delegate = delegate;%0A    %7D%0A%0A    @Override%0A    public 
 Object e
@@ -1759,20 +1759,25 @@
 ) %7B%0A    
-if (
+  return 
 instance
@@ -1781,31 +1781,54 @@
 nce 
-=
+!
 = null
-) %7B%0A      return
+ ? delegate.extract(member, instance) :
  nul
@@ -1840,62 +1840,222 @@
   %7D%0A
-%0A
   
-  return extractor.extract(member, instance)
+%7D%0A%0A  private static class UnwrapOptional implements MemberExtractor %7B%0A%0A    private final MemberExtractor delegate;%0A%0A    private UnwrapOptional(MemberExtractor delegate) %7B%0A      this.delegate = delegate
 ;%0A  
+ 
+ 
 %7D%0A%0A
+  
   @S
@@ -2089,74 +2089,147 @@
 )%0A  
-private static Object maybeUnwrapOptional(Object maybeOptional) %7B%0A
+  @Override%0A    public Object extract(Member member, Object instance) %7B%0A      Object maybeOptional = delegate.extract(member, instance);%0A  
     
@@ -2273,24 +2273,26 @@
 l)) %7B%0A      
+ 
+ 
 return  ((Op
@@ -2328,27 +2328,31 @@
 (null);%0A    
+  
 %7D%0A%0A
+  
     if (mayb
@@ -2409,24 +2409,26 @@
 al) %7B%0A      
+  
 return ((com
@@ -2482,27 +2482,30 @@
 Null();%0A    
-%7D%0A%0A
+  %7D%0A  
     return m
@@ -2520,12 +2520,19 @@
 onal;%0A  
-%7D
+  %7D%0A  %7D%0A
 %0A%7D%0A

Refactor ReflectionExtractor to use composable extractors (NullSafe and UnwrapOptional)