001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.logging.log4j.core.config.plugins.visitors;
019
020 import java.lang.reflect.Array;
021 import java.util.ArrayList;
022 import java.util.Arrays;
023 import java.util.Collection;
024 import java.util.List;
025
026 import org.apache.logging.log4j.core.LogEvent;
027 import org.apache.logging.log4j.core.config.Configuration;
028 import org.apache.logging.log4j.core.config.Node;
029 import org.apache.logging.log4j.core.config.plugins.PluginElement;
030 import org.apache.logging.log4j.core.config.plugins.util.PluginType;
031
032 /**
033 * PluginVisitor implementation for {@link PluginElement}. Supports arrays as well as singular values.
034 */
035 public class PluginElementVisitor extends AbstractPluginVisitor<PluginElement> {
036 public PluginElementVisitor() {
037 super(PluginElement.class);
038 }
039
040 @Override
041 public Object visit(final Configuration configuration, final Node node, final LogEvent event,
042 final StringBuilder log) {
043 final String name = this.annotation.value();
044 if (this.conversionType.isArray()) {
045 setConversionType(this.conversionType.getComponentType());
046 final List<Object> values = new ArrayList<Object>();
047 final Collection<Node> used = new ArrayList<Node>();
048 log.append("={");
049 boolean first = true;
050 for (final Node child : node.getChildren()) {
051 final PluginType<?> childType = child.getType();
052 if (name.equalsIgnoreCase(childType.getElementName()) ||
053 this.conversionType.isAssignableFrom(childType.getPluginClass())) {
054 if (!first) {
055 log.append(", ");
056 }
057 first = false;
058 used.add(child);
059 final Object childObject = child.getObject();
060 if (childObject == null) {
061 LOGGER.error("Null object returned for {} in {}.", child.getName(), node.getName());
062 continue;
063 }
064 if (childObject.getClass().isArray()) {
065 log.append(Arrays.toString((Object[]) childObject)).append('}');
066 return childObject;
067 }
068 log.append(child.toString());
069 values.add(childObject);
070 }
071 }
072 log.append('}');
073 // note that we need to return an empty array instead of null if the types are correct
074 if (!values.isEmpty() && !this.conversionType.isAssignableFrom(values.get(0).getClass())) {
075 LOGGER.error("Attempted to assign attribute {} to list of type {} which is incompatible with {}.",
076 name, values.get(0).getClass(), this.conversionType);
077 return null;
078 }
079 node.getChildren().removeAll(used);
080 // we need to use reflection here because values.toArray() will cause type errors at runtime
081 final Object[] array = (Object[]) Array.newInstance(this.conversionType, values.size());
082 for (int i = 0; i < array.length; i++) {
083 array[i] = values.get(i);
084 }
085 return array;
086 }
087 final Node namedNode = findNamedNode(name, node.getChildren());
088 if (namedNode == null) {
089 log.append("null");
090 return null;
091 }
092 log.append(namedNode.getName()).append('(').append(namedNode.toString()).append(')');
093 node.getChildren().remove(namedNode);
094 return namedNode.getObject();
095 }
096
097 private Node findNamedNode(final String name, final Iterable<Node> children) {
098 for (final Node child : children) {
099 final PluginType<?> childType = child.getType();
100 if (name.equalsIgnoreCase(childType.getElementName()) ||
101 this.conversionType.isAssignableFrom(childType.getPluginClass())) {
102 // FIXME: check child.getObject() for null?
103 // doing so would be more consistent with the array version
104 return child;
105 }
106 }
107 return null;
108 }
109 }