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.log4j.or; 019 020import org.apache.log4j.spi.RendererSupport; 021import org.apache.log4j.helpers.LogLog; 022import org.apache.log4j.helpers.Loader; 023import org.apache.log4j.helpers.OptionConverter; 024import java.util.Hashtable; 025 026/** 027 Map class objects to an {@link ObjectRenderer}. 028 029 @author Ceki Gülcü 030 @since version 1.0 */ 031public class RendererMap { 032 033 Hashtable map; 034 035 static ObjectRenderer defaultRenderer = new DefaultRenderer(); 036 037 public 038 RendererMap() { 039 map = new Hashtable(); 040 } 041 042 /** 043 Add a renderer to a hierarchy passed as parameter. 044 */ 045 static 046 public 047 void addRenderer(RendererSupport repository, String renderedClassName, 048 String renderingClassName) { 049 LogLog.debug("Rendering class: ["+renderingClassName+"], Rendered class: ["+ 050 renderedClassName+"]."); 051 ObjectRenderer renderer = (ObjectRenderer) 052 OptionConverter.instantiateByClassName(renderingClassName, 053 ObjectRenderer.class, 054 null); 055 if(renderer == null) { 056 LogLog.error("Could not instantiate renderer ["+renderingClassName+"]."); 057 return; 058 } else { 059 try { 060 Class renderedClass = Loader.loadClass(renderedClassName); 061 repository.setRenderer(renderedClass, renderer); 062 } catch(ClassNotFoundException e) { 063 LogLog.error("Could not find class ["+renderedClassName+"].", e); 064 } 065 } 066 } 067 068 069 /** 070 Find the appropriate renderer for the class type of the 071 <code>o</code> parameter. This is accomplished by calling the 072 {@link #get(Class)} method. Once a renderer is found, it is 073 applied on the object <code>o</code> and the result is returned 074 as a {@link String}. */ 075 public 076 String findAndRender(Object o) { 077 if(o == null) 078 return null; 079 else 080 return get(o.getClass()).doRender(o); 081 } 082 083 084 /** 085 Syntactic sugar method that calls {@link #get(Class)} with the 086 class of the object parameter. */ 087 public 088 ObjectRenderer get(Object o) { 089 if(o == null) 090 return null; 091 else 092 return get(o.getClass()); 093 } 094 095 096 /** 097 Search the parents of <code>clazz</code> for a renderer. The 098 renderer closest in the hierarchy will be returned. If no 099 renderers could be found, then the default renderer is returned. 100 101 <p>The search first looks for a renderer configured for 102 <code>clazz</code>. If a renderer could not be found, then the 103 search continues by looking at all the interfaces implemented by 104 <code>clazz</code> including the super-interfaces of each 105 interface. If a renderer cannot be found, then the search looks 106 for a renderer defined for the parent (superclass) of 107 <code>clazz</code>. If that fails, then all the interfaces 108 implemented by the parent of <code>clazz</code> are searched and 109 so on. 110 111 <p>For example, if A0, A1, A2 are classes and X0, X1, X2, Y0, Y1 112 are interfaces where A2 extends A1 which in turn extends A0 and 113 similarly X2 extends X1 which extends X0 and Y1 extends Y0. Let 114 us also assume that A1 implements the Y0 interface and that A2 115 implements the X2 interface. 116 117 <p>The table below shows the results returned by the 118 <code>get(A2.class)</code> method depending on the renderers 119 added to the map. 120 121 <p><table border="1"> 122 <tr><th>Added renderers</th><th>Value returned by <code>get(A2.class)</code></th> 123 124 <tr><td><code>A0Renderer</code> 125 <td align="center"><code>A0Renderer</code> 126 127 <tr><td><code>A0Renderer, A1Renderer</code> 128 <td align="center"><code>A1Renderer</code> 129 130 <tr><td><code>X0Renderer</code> 131 <td align="center"><code>X0Renderer</code> 132 133 <tr><td><code>A1Renderer, X0Renderer</code> 134 <td align="center"><code>X0Renderer</code> 135 136 </table> 137 138 <p>This search algorithm is not the most natural, although it is 139 particularly easy to implement. Future log4j versions 140 <em>may</em> implement a more intuitive search 141 algorithm. However, the present algorithm should be acceptable in 142 the vast majority of circumstances. 143 144 */ 145 public 146 ObjectRenderer get(Class clazz) { 147 //System.out.println("\nget: "+clazz); 148 ObjectRenderer r = null; 149 for(Class c = clazz; c != null; c = c.getSuperclass()) { 150 //System.out.println("Searching for class: "+c); 151 r = (ObjectRenderer) map.get(c); 152 if(r != null) { 153 return r; 154 } 155 r = searchInterfaces(c); 156 if(r != null) 157 return r; 158 } 159 return defaultRenderer; 160 } 161 162 ObjectRenderer searchInterfaces(Class c) { 163 //System.out.println("Searching interfaces of class: "+c); 164 165 ObjectRenderer r = (ObjectRenderer) map.get(c); 166 if(r != null) { 167 return r; 168 } else { 169 Class[] ia = c.getInterfaces(); 170 for(int i = 0; i < ia.length; i++) { 171 r = searchInterfaces(ia[i]); 172 if(r != null) 173 return r; 174 } 175 } 176 return null; 177 } 178 179 180 public 181 ObjectRenderer getDefaultRenderer() { 182 return defaultRenderer; 183 } 184 185 186 public 187 void clear() { 188 map.clear(); 189 } 190 191 /** 192 Register an {@link ObjectRenderer} for <code>clazz</code>. 193 */ 194 public 195 void put(Class clazz, ObjectRenderer or) { 196 map.put(clazz, or); 197 } 198}