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 018package org.apache.logging.log4j.core.config.plugins.visitors; 019 020import java.lang.reflect.Array; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.Collection; 024import java.util.List; 025 026import org.apache.logging.log4j.core.LogEvent; 027import org.apache.logging.log4j.core.config.Configuration; 028import org.apache.logging.log4j.core.config.Node; 029import org.apache.logging.log4j.core.config.plugins.PluginElement; 030import 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 */ 035public 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}