/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.logging.log4j.core.config.plugins.visitors;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.Node;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.util.PluginType;

/**
 * PluginVisitor implementation for {@link PluginElement}. Supports arrays as well as singular values.
 */
public class PluginElementVisitor extends AbstractPluginVisitor<PluginElement> {
    public PluginElementVisitor() {
        super(PluginElement.class);
    }

    @Override
    public Object visit(final Configuration configuration, final Node node, final LogEvent event) {
        final String name = this.annotation.value();
        if (this.conversionType.isArray()) {
            setConversionType(this.conversionType.getComponentType());
            final List<Object> values = new ArrayList<Object>();
            final Collection<Node> used = new ArrayList<Node>();
            for (final Node child : node.getChildren()) {
                final PluginType<?> childType = child.getType();
                if (name.equalsIgnoreCase(childType.getElementName()) ||
                    this.conversionType.isAssignableFrom(childType.getPluginClass())) {
                    used.add(child);
                    final Object childObject = child.getObject();
                    if (childObject == null) {
                        LOGGER.error("Null object returned for {} in {}.", child.getName(), node.getName());
                        continue;
                    }
                    if (childObject.getClass().isArray()) {
                        final Object[] o = (Object[]) childObject;
                        LOGGER.debug("{}={}", name, Arrays.toString(o));
                        return childObject;
                    }
                    values.add(childObject);
                }
            }
            // note that we need to return an empty array instead of null if the types are correct
            if (!values.isEmpty() && !this.conversionType.isAssignableFrom(values.get(0).getClass())) {
                LOGGER.error("Attempted to assign attribute {} to list of type {} which is incompatible with {}.",
                    name, values.get(0).getClass(), this.conversionType);
                return null;
            }
            node.getChildren().removeAll(used);
            LOGGER.debug("{}={}", name, values);
            // we need to use reflection here because values.toArray() will cause type errors at runtime
            final Object[] array = (Object[]) Array.newInstance(this.conversionType, values.size());
            for (int i = 0; i < array.length; i++) {
                array[i] = values.get(i);
            }
            return array;
        } else {
            final Node namedNode = findNamedNode(name, node.getChildren());
            if (namedNode == null) {
                return null;
            }
            LOGGER.debug("{}({})", namedNode.getName(), namedNode.toString());
            node.getChildren().remove(namedNode);
            return namedNode.getObject();
        }
    }

    private Node findNamedNode(final String name, final Iterable<Node> children) {
        for (final Node child : children) {
            final PluginType<?> childType = child.getType();
            if (name.equalsIgnoreCase(childType.getElementName()) ||
                this.conversionType.isAssignableFrom(childType.getPluginClass())) {
                // FIXME: check child.getObject() for null?
                // doing so would be more consistent with the array version
                return child;
            }
        }
        return null;
    }
}

@@ -3802,20 +3802,9 @@
    %7D
- else %7B%0A    
+%0A
     
@@ -3871,36 +3871,32 @@
 ren());%0A        
-    
 if (namedNode ==
@@ -3908,36 +3908,32 @@
 ) %7B%0A            
-    
 return null;%0A   
@@ -3921,36 +3921,32 @@
    return null;%0A
-    
         %7D%0A      
@@ -3939,36 +3939,32 @@
       %7D%0A        
-    
 LOGGER.debug(%22%7B%7D
@@ -4014,36 +4014,32 @@
 ing());%0A        
-    
 node.getChildren
@@ -4060,36 +4060,32 @@
 dNode);%0A        
-   
- 
 return namedNode
@@ -4098,26 +4098,16 @@
 ject();%0A
-        %7D%0A
     %7D%0A%0A 

Statement unnecessarily nested within else clause.