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}