protected void buildRecurse()

in grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/config/ConfigurationBuilder.groovy [139:400]


    protected void buildRecurse(Object builder, List<Class> builderQueue, Object fallBackConfig, String startingPrefix) {

        List<Class> hierarchy = toHierarchy(builder.getClass())

        startBuild(builder, startingPrefix)

        for(Class builderClass in hierarchy) {

            def methods = builderClass.declaredMethods
            for (method in methods) {
                def methodName = method.name
                if (!Modifier.isPublic(method.modifiers) || method.isSynthetic() || IGNORE_METHODS.contains(methodName)) {
                    continue
                }
                if (method.declaringClass != builderClass) {
                    continue
                }
                def parameterTypes = method.parameterTypes

                String settingName

                boolean hasBuilderPrefix = builderMethodPrefix != null

                if (hasBuilderPrefix && methodName.startsWith(builderMethodPrefix)) {
                    settingName = methodName.substring(builderMethodPrefix.size()).uncapitalize()
                }
                else if (hasBuilderPrefix) {
                    continue
                }
                else if (!hasBuilderPrefix &&
                        ((org.grails.datastore.mapping.reflect.ReflectionUtils.isGetter(methodName, parameterTypes) && method.returnType.getAnnotation(Builder) == null) ||
                                org.grails.datastore.mapping.reflect.ReflectionUtils.isSetter(methodName, parameterTypes))) {
                    // don't process getters or setters, unless the getter returns a builder
                    continue
                }
                else {
                    settingName = methodName
                }

                String propertyPath = startingPrefix ? "${startingPrefix}.${settingName}" : settingName

                if (parameterTypes.length == 1) {
                    Class argType = parameterTypes[0]

                    def builderMethod = ReflectionUtils.findMethod(argType, 'builder')
                    if (builderMethod != null && Modifier.isStatic(builderMethod.modifiers)) {
                        if (propertyResolver.containsProperty(propertyPath)) {
                            Method existingGetter = ReflectionUtils.findMethod(builderClass, NameUtils.getGetterName(methodName))
                            def newBuilder

                            if (existingGetter != null) {
                                newBuilder = existingGetter.invoke(builder)
                            }
                            if (newBuilder == null) {
                                newBuilder = builderMethod.invoke(argType)
                            }

                            newChildBuilder(newBuilder, propertyPath)

                            Object fallBackChildConfig = getFallBackValue(fallBackConfig, settingName)
                            if (!builderQueue.contains(newBuilder.class)) {
                                builderQueue.add(newBuilder.class)
                                buildRecurse(newBuilder, builderQueue, fallBackChildConfig, propertyPath)
                                builderQueue.remove(newBuilder.class)

                                def buildMethod = ReflectionUtils.findMethod(newBuilder.getClass(), 'build')
                                if (buildMethod != null) {
                                    try {
                                        method.invoke(builder, buildMethod.invoke(newBuilder))
                                    } catch (Throwable e) {
                                        log.error("build method threw exception", e)
                                    }
                                } else {
                                    method.invoke(builder, newBuilder)
                                }
                            }
                        }

                        continue
                    }

                    def buildMethod = ReflectionUtils.findMethod(argType, 'build')
                    if (buildMethod != null) {
                        Method existingGetter = ReflectionUtils.findMethod(builderClass, NameUtils.getGetterName(methodName))
                        def newBuilder

                        if (existingGetter != null) {
                            newBuilder = existingGetter.invoke(builder)

                            if (newBuilder != null) {
                                Object fallBackChildConfig = getFallBackValue(fallBackConfig, settingName)
                                newBuilder = newChildBuilderForFallback(newBuilder, fallBackChildConfig)
                                if (!builderQueue.contains(newBuilder.class)) {
                                    builderQueue.add(newBuilder.class)
                                    buildRecurse(newBuilder, builderQueue, fallBackChildConfig, propertyPath)
                                    builderQueue.remove(newBuilder.class)
                                    newChildBuilder(newBuilder, propertyPath)
                                    method.invoke(builder, newBuilder)
                                }
                                continue
                            }
                        }
                    }

                    Builder builderAnnotation = argType.getAnnotation(Builder)
                    if (builderAnnotation != null && builderAnnotation.builderStrategy() == SimpleStrategy) {
                        Method existingGetter = ReflectionUtils.findMethod(builderClass, NameUtils.getGetterName(methodName))
                        def newBuilder
                        if (existingGetter != null) {
                            newBuilder = existingGetter.invoke(builder)
                        }
                        if (newBuilder == null) {
                            newBuilder = argType.newInstance()
                        }

                        if (newBuilder instanceof Map) {
                            Map subMap = propertyResolver.getProperty(propertyPath, Map, Collections.emptyMap())
                            if (!subMap.isEmpty()) {
                                ((Map) newBuilder).putAll(subMap)
                            }
                        }

                        newChildBuilder(newBuilder, propertyPath)

                        Object fallBackChildConfig = getFallBackValue(fallBackConfig, methodName)
                        if (!builderQueue.contains(newBuilder.class)) {
                            builderQueue.add(newBuilder.class)
                            buildRecurse(newBuilder, builderQueue, fallBackChildConfig, propertyPath)
                            builderQueue.remove(newBuilder.class)
                            method.invoke(builder, newBuilder)
                        }
                        continue
                    }

                    if (ConfigurationBuilder.isAssignableFrom(argType)) {
                        try {
                            Method existingGetter = ReflectionUtils.findMethod(builderClass, NameUtils.getGetterName(methodName))
                            ConfigurationBuilder newBuilder
                            if (existingGetter != null) {
                                newBuilder = (ConfigurationBuilder) existingGetter.invoke(builder)
                            }
                            if (newBuilder == null) {

                                if (fallBackConfig != null && builderClass.isInstance(fallBackConfig)) {

                                    ConfigurationBuilder fallbackBuilder = (ConfigurationBuilder) existingGetter.invoke(fallBackConfig)
                                    if (fallbackBuilder != null) {
                                        newBuilder = (ConfigurationBuilder) argType.newInstance(this.propertyResolver, propertyPath, fallbackBuilder.build())
                                    } else {
                                        newBuilder = (ConfigurationBuilder) argType.newInstance(this.propertyResolver, propertyPath)
                                    }
                                } else {
                                    newBuilder = (ConfigurationBuilder) argType.newInstance(this.propertyResolver, propertyPath)
                                }


                            }
                            newChildBuilder(newBuilder, propertyPath)
                            method.invoke(builder, newBuilder)
                        } catch (Throwable e) {
                            throw new ConfigurationException("Cannot read configuration for path $propertyPath: $e.message", e)
                        }
                        continue
                    }
                } else if (methodName.startsWith("get") && parameterTypes.length == 0) {
                    if (method.returnType.getAnnotation(Builder)) {
                        def childBuilder = method.invoke(builder)
                        if (childBuilder != null) {
                            Object fallBackChildConfig = null
                            if (fallBackConfig != null) {
                                Method fallbackGetter = ReflectionUtils.findMethod(fallBackConfig.getClass(), methodName)
                                if (fallbackGetter != null) {
                                    fallBackChildConfig = fallbackGetter.invoke(fallBackConfig)
                                }
                            }

                            String getterPropertyPath = startingPrefix ? "${startingPrefix}.${NameUtils.getPropertyNameForGetterOrSetter(methodName)}" : NameUtils.getPropertyNameForGetterOrSetter(methodName)
                            if (!builderQueue.contains(childBuilder.class)) {
                                builderQueue.add(childBuilder.class)
                                buildRecurse(childBuilder, builderQueue, fallBackChildConfig, getterPropertyPath)
                                builderQueue.remove(childBuilder.class)
                            }
                            continue
                        }
                    }
                } else if (parameterTypes.length == 0) {
                    def value = propertyResolver.getProperty(propertyPath, Boolean, false)
                    if (value) {
                        try {
                            method.invoke(builder)
                        } catch (Throwable e) {
                            throw new ConfigurationException("Error executing method for path $propertyPath: $e.message", e)
                        }
                    }
                    continue
                }

                List<Object> args = []

                boolean appendArgName = parameterTypes.length > 1
                int argIndex = 0

                for (Class argType: parameterTypes) {
                    String propertyPathForArg = propertyPath
                    if (appendArgName) {
                        propertyPathForArg += ".arg${argIndex}"
                    }
                    argIndex++
                    def valueOfMethod = ReflectionUtils.findMethod(argType, 'valueOf')
                    if (valueOfMethod != null && Modifier.isStatic(valueOfMethod.modifiers)) {
                        try {
                            def value = propertyResolver.getProperty(propertyPathForArg, "")
                            if (value) {
                                def converted = valueOfMethod.invoke(argType, value)
                                args.add(converted)
                            }
                        } catch (Throwable e) {
                            throw new ConfigurationException("Cannot read configuration for path $propertyPathForArg: $e.message", e)
                        }
                    }
                    else {
                        Object fallBackValue = getFallBackValue(fallBackConfig, settingName)

                        def value
                        try {
                            value = propertyResolver.getProperty(propertyPathForArg, argType, fallBackValue)
                        } catch (ConversionFailedException e) {
                            if(argType.isEnum()) {
                                value = propertyResolver.getProperty(propertyPathForArg, String)
                                if (value != null) {
                                    try {
                                        value = Enum.valueOf((Class)argType, value.toUpperCase())
                                    } catch (Throwable e2) {
                                        // ignore e2 and throw original
                                        throw new ConfigurationException("Invalid value for setting [$propertyPathForArg]: $e.message", e)
                                    }
                                }
                                else {
                                    throw new ConfigurationException("Invalid value for setting [$propertyPathForArg]: $e.message", e)
                                }
                            }
                            else {
                                throw new ConfigurationException("Invalid value for setting [$propertyPathForArg]: $e.message", e)
                            }
                        }
                        if (value != null) {
                            log.debug("Resolved value [{}] for setting [{}]", value, propertyPathForArg)
                            args.add(value)
                        }

                    }
                }

                if (args) {
                    ReflectionUtils.makeAccessible(method)
                    ReflectionUtils.invokeMethod(method, builder, args.toArray())
                }
            }

        }

    }