/**
 *
 * Copyright (c) 2006-2015, Speedment, Inc. All Rights Reserved.
 *
 * 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 com.speedment.orm.config.model.impl.utils;

import com.speedment.orm.config.model.External;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 *
 * @author Emil Forslund
 */
public final class MethodsParser {
    
    private MethodsParser() {}

    private static final Predicate<Method> 
        METHOD_IS_PUBLIC = (m) -> Modifier.isPublic(m.getModifiers()),
        METHOD_IS_GETTER = (m) -> m.getParameterCount() == 0 && (m.getName().startsWith("get") || m.getName().startsWith("is")),
        METHOD_IS_EXTERNAL = MethodsParser::isExternal;

    public static Stream<Method> streamOfExternal(Class<?> clazz) {
        return getMethods(clazz,
            METHOD_IS_PUBLIC
            .and(METHOD_IS_GETTER)
            .and(METHOD_IS_EXTERNAL)
        ).stream();
    }

    private static Set<Method> getMethods(Class<?> clazz, Predicate<Method> filter) {
        return addMethods(new HashSet<>(), clazz, filter);
    }

    private static boolean isExternal(Method method) {
        return isExternal(method, method.getDeclaringClass());
    }

    private static boolean isExternal(final Method method, final Class<?> clazz) {
        if (method == null || clazz == null) {
            return false;
        }
        if (method.getAnnotation(External.class) != null) {
            return true;
        }
        // Also try the superClass and all the interfaces it implements
        final List<Class<?>> classCandidates = new ArrayList<>(Arrays.asList(clazz.getInterfaces()));
        final Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            classCandidates.add(superClass);
        }
        for (final Class<?> classCandidate : classCandidates) {
            try {
                if (isExternal(classCandidate.getMethod(method.getName(), method.getParameterTypes()), classCandidate)) {
                    return true;
                }
            } catch (NoSuchMethodException | SecurityException e) {
                // ignore
            }
        }
        return false;
    }

    private static Set<Method> addMethods(Set<Method> methods, Class<?> clazz, Predicate<Method> filter) {
        if (clazz == Object.class) {
            return methods;
        }
        Stream.of(clazz.getDeclaredMethods())
            .filter(filter)
            .forEach(methods::add);
        addMethods(methods, clazz.getSuperclass(), filter); // Recursively add the superclass methods
        return methods;
    }
}

@@ -1900,36 +1900,39 @@
 ;%0A    %7D%0A
+    
 %0A    p
-rivate
+ublic
  static 
 boolean 
@@ -1923,34 +1923,36 @@
  static 
-boolean is
+External get
 External
 (final M
@@ -1943,16 +1943,19 @@
 External
+For
 (final M
@@ -2054,29 +2054,28 @@
      return 
-false
+null
 ;%0A        %7D%0A
@@ -2078,28 +2078,63 @@
   %7D%0A        
-if (
+%0A        final External e = (External) 
 method.getAn
@@ -2157,16 +2157,31 @@
 l.class)
+;%0A        if (e
  != null
@@ -2195,35 +2195,32 @@
          return 
-tru
 e;%0A        %7D%0A   
@@ -2445,24 +2445,33 @@
 perclass();%0A
+        %0A
         if (
@@ -2539,32 +2539,41 @@
 ass);%0A        %7D%0A
+        %0A
         for (fin
@@ -2658,22 +2658,26 @@
     
-if (is
+return get
 External
 (cla
@@ -2672,16 +2672,19 @@
 External
+For
 (classCa
@@ -2767,62 +2767,9 @@
 ate)
-) %7B%0A                    return true;%0A                %7D
+;
 %0A   
@@ -2829,19 +2829,21 @@
 eption e
+x
 ) %7B
+%7D
 %0A       
@@ -2847,70 +2847,182 @@
     
- 
+%7D%0A
        
-// ignore%0A            %7D%0A        %7D%0A        return false
+ %0A        return null;%0A    %7D%0A%0A    private static boolean isExternal(final Method method, final Class%3C?%3E clazz) %7B%0A        return getExternalFor(method, clazz) != null
 ;%0A  

Add method for accessing external annotation