RendererFactory.java
001 /**
002  * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003  */
004 package net.sourceforge.pmd.renderers;
005 
006 import java.lang.reflect.Constructor;
007 import java.lang.reflect.InvocationTargetException;
008 import java.lang.reflect.Modifier;
009 import java.util.Collections;
010 import java.util.Map;
011 import java.util.Properties;
012 import java.util.TreeMap;
013 import java.util.logging.Logger;
014 
015 /**
016  * This class handles the creation of Renderers.
017  @see Renderer
018  */
019 public class RendererFactory {
020 
021     private static final Logger LOG = Logger.getLogger(RendererFactory.class.getName());
022 
023     public static final Map<String, Class<? extends Renderer>> REPORT_FORMAT_TO_RENDERER;
024     static {
025   Map<String, Class<? extends Renderer>> map = new TreeMap<String, Class<? extends Renderer>>();
026   map.put(XMLRenderer.NAME, XMLRenderer.class);
027   map.put(IDEAJRenderer.NAME, IDEAJRenderer.class);
028   map.put(TextColorRenderer.NAME, TextColorRenderer.class);
029   map.put("papari", TextColorRenderer.class)// TODO Remove when we drop backward compatibility.
030   map.put(TextRenderer.NAME, TextRenderer.class);
031   map.put(TextPadRenderer.NAME, TextPadRenderer.class);
032   map.put(EmacsRenderer.NAME, EmacsRenderer.class);
033   map.put(CSVRenderer.NAME, CSVRenderer.class);
034   map.put(HTMLRenderer.NAME, HTMLRenderer.class);
035   map.put("nicehtml", XSLTRenderer.class)// TODO Remove when we drop backward compatibility.
036   map.put(XSLTRenderer.NAME, XSLTRenderer.class);
037   map.put(YAHTMLRenderer.NAME, YAHTMLRenderer.class);
038   map.put(SummaryHTMLRenderer.NAME, SummaryHTMLRenderer.class);
039   map.put(VBHTMLRenderer.NAME, VBHTMLRenderer.class);
040   REPORT_FORMAT_TO_RENDERER = Collections.unmodifiableMap(map);
041     }
042 
043     /**
044      * Construct an instance of a Renderer based on report format name.
045      @param reportFormat The report format name.
046      @param properties Initialization properties for the corresponding Renderer.
047      @return A Renderer instance.
048      */
049     public static Renderer createRenderer(String reportFormat, Properties properties) {
050   Class<? extends Renderer> rendererClass = getRendererClass(reportFormat);
051   Constructor<? extends Renderer> constructor = getRendererConstructor(rendererClass);
052 
053   Renderer renderer;
054   try {
055       if (constructor.getParameterTypes().length > 0) {
056     renderer = constructor.newInstance(properties);
057       else {
058     renderer = constructor.newInstance();
059       }
060   catch (InstantiationException e) {
061       throw new IllegalArgumentException("Unable to construct report renderer class: " + e.getLocalizedMessage());
062   catch (IllegalAccessException e) {
063       throw new IllegalArgumentException("Unable to construct report renderer class: " + e.getLocalizedMessage());
064   catch (InvocationTargetException e) {
065       throw new IllegalArgumentException("Unable to construct report renderer class: "
066         + e.getTargetException().getLocalizedMessage());
067   }
068   // Warn about legacy report format usages
069   if (REPORT_FORMAT_TO_RENDERER.containsKey(reportFormat&& !reportFormat.equals(renderer.getName())) {
070       LOG.warning("Report format '" + reportFormat + "' is deprecated, and has been replaced with '"
071         + renderer.getName()
072         "'. Future versions of PMD will remove support for this deprecated Report format usage.");
073   }
074   return renderer;
075     }
076 
077     @SuppressWarnings("unchecked")
078     private static Class<? extends Renderer> getRendererClass(String reportFormat) {
079   Class<? extends Renderer> rendererClass = REPORT_FORMAT_TO_RENDERER.get(reportFormat);
080 
081   // Look up a custom renderer class
082   if (rendererClass == null && !"".equals(reportFormat)) {
083       try {
084     Class<?> clazz = Class.forName(reportFormat);
085     if (!Renderer.class.isAssignableFrom(clazz)) {
086         throw new IllegalArgumentException("Custom report renderer class does not implement the "
087           + Renderer.class.getName() " interface.");
088     else {
089         rendererClass = (Class<? extends Renderer>clazz;
090     }
091       catch (ClassNotFoundException e) {
092     throw new IllegalArgumentException("Can't find the custom format " + reportFormat + ": "
093       + e.getClass().getName());
094       }
095   }
096   return rendererClass;
097     }
098 
099     private static Constructor<? extends Renderer> getRendererConstructor(Class<? extends Renderer> rendererClass) {
100   Constructor<? extends Renderer> constructor = null;
101 
102   // 1) Properties constructor?
103   try {
104       constructor = rendererClass.getConstructor(Properties.class);
105       if (!Modifier.isPublic(constructor.getModifiers())) {
106     constructor = null;
107       }
108   catch (NoSuchMethodException e) {
109       // Ok
110   }
111 
112   // 2) No-arg constructor?
113   try {
114       constructor = rendererClass.getConstructor();
115       if (!Modifier.isPublic(constructor.getModifiers())) {
116     constructor = null;
117       }
118   catch (NoSuchMethodException e2) {
119       // Ok
120   }
121 
122   if (constructor == null) {
123       throw new IllegalArgumentException(
124         "Unable to find either a public java.util.Properties or no-arg constructors for Renderer class: "
125           + rendererClass.getName());
126   }
127   return constructor;
128     }
129 }