1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.log4j.or;
19
20 import org.apache.log4j.spi.RendererSupport;
21 import org.apache.log4j.helpers.LogLog;
22 import org.apache.log4j.helpers.Loader;
23 import org.apache.log4j.helpers.OptionConverter;
24 import java.util.Hashtable;
25
26 /**
27 Map class objects to an {@link ObjectRenderer}.
28
29 @author Ceki Gülcü
30 @since version 1.0 */
31 public class RendererMap {
32
33 Hashtable map;
34
35 static ObjectRenderer defaultRenderer = new DefaultRenderer();
36
37 public
38 RendererMap() {
39 map = new Hashtable();
40 }
41
42 /**
43 Add a renderer to a hierarchy passed as parameter.
44 */
45 static
46 public
47 void addRenderer(RendererSupport repository, String renderedClassName,
48 String renderingClassName) {
49 LogLog.debug("Rendering class: ["+renderingClassName+"], Rendered class: ["+
50 renderedClassName+"].");
51 ObjectRenderer renderer = (ObjectRenderer)
52 OptionConverter.instantiateByClassName(renderingClassName,
53 ObjectRenderer.class,
54 null);
55 if(renderer == null) {
56 LogLog.error("Could not instantiate renderer ["+renderingClassName+"].");
57 return;
58 } else {
59 try {
60 Class renderedClass = Loader.loadClass(renderedClassName);
61 repository.setRenderer(renderedClass, renderer);
62 } catch(ClassNotFoundException e) {
63 LogLog.error("Could not find class ["+renderedClassName+"].", e);
64 }
65 }
66 }
67
68
69 /**
70 Find the appropriate renderer for the class type of the
71 <code>o</code> parameter. This is accomplished by calling the
72 {@link #get(Class)} method. Once a renderer is found, it is
73 applied on the object <code>o</code> and the result is returned
74 as a {@link String}. */
75 public
76 String findAndRender(Object o) {
77 if(o == null)
78 return null;
79 else
80 return get(o.getClass()).doRender(o);
81 }
82
83
84 /**
85 Syntactic sugar method that calls {@link #get(Class)} with the
86 class of the object parameter. */
87 public
88 ObjectRenderer get(Object o) {
89 if(o == null)
90 return null;
91 else
92 return get(o.getClass());
93 }
94
95
96 /**
97 Search the parents of <code>clazz</code> for a renderer. The
98 renderer closest in the hierarchy will be returned. If no
99 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 }
199