View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.renderers;
5   
6   import java.lang.reflect.Constructor;
7   import java.lang.reflect.InvocationTargetException;
8   import java.lang.reflect.Modifier;
9   import java.util.Collections;
10  import java.util.Map;
11  import java.util.Properties;
12  import java.util.TreeMap;
13  import java.util.logging.Level;
14  import java.util.logging.Logger;
15  
16  import net.sourceforge.pmd.PropertyDescriptor;
17  
18  /**
19   * This class handles the creation of Renderers.
20   * @see Renderer
21   */
22  public class RendererFactory {
23  
24      private static final Logger LOG = Logger.getLogger(RendererFactory.class.getName());
25  
26      public static final Map<String, Class<? extends Renderer>> REPORT_FORMAT_TO_RENDERER;
27      static {
28  	Map<String, Class<? extends Renderer>> map = new TreeMap<String, Class<? extends Renderer>>();
29  	map.put(CodeClimateRenderer.NAME, CodeClimateRenderer.class);
30  	map.put(XMLRenderer.NAME, XMLRenderer.class);
31  	map.put(IDEAJRenderer.NAME, IDEAJRenderer.class);
32  	map.put(TextColorRenderer.NAME, TextColorRenderer.class);
33  	map.put(TextRenderer.NAME, TextRenderer.class);
34  	map.put(TextPadRenderer.NAME, TextPadRenderer.class);
35  	map.put(EmacsRenderer.NAME, EmacsRenderer.class);
36  	map.put(CSVRenderer.NAME, CSVRenderer.class);
37  	map.put(HTMLRenderer.NAME, HTMLRenderer.class);
38  	map.put(XSLTRenderer.NAME, XSLTRenderer.class);
39  	map.put(YAHTMLRenderer.NAME, YAHTMLRenderer.class);
40  	map.put(SummaryHTMLRenderer.NAME, SummaryHTMLRenderer.class);
41  	map.put(VBHTMLRenderer.NAME, VBHTMLRenderer.class);
42  	REPORT_FORMAT_TO_RENDERER = Collections.unmodifiableMap(map);
43      }
44  
45      /**
46       * Construct an instance of a Renderer based on report format name.
47       * @param reportFormat The report format name.
48       * @param properties Initialization properties for the corresponding Renderer.
49       * @return A Renderer instance.
50       */
51      public static Renderer createRenderer(String reportFormat, Properties properties) {
52  	Class<? extends Renderer> rendererClass = getRendererClass(reportFormat);
53  	Constructor<? extends Renderer> constructor = getRendererConstructor(rendererClass);
54  
55  	Renderer renderer;
56  	try {
57  	    if (constructor.getParameterTypes().length > 0) {
58  		LOG.warning("The renderer uses a deprecated mechanism to use the properties. Please define the needed properties with this.definePropertyDescriptor(..).");
59  		renderer = constructor.newInstance(properties);
60  	    } else {
61  		renderer = constructor.newInstance();
62  
63  		for (PropertyDescriptor<?> prop : renderer.getPropertyDescriptors()) {
64  		    String value = properties.getProperty(prop.name());
65  		    if (value != null) {
66  			@SuppressWarnings("unchecked")
67  			PropertyDescriptor<Object> prop2 = (PropertyDescriptor<Object>)prop;
68  			Object valueFrom = prop2.valueFrom(value);
69  			renderer.setProperty(prop2, valueFrom);
70  		    }
71  		}
72  	    }
73  	} catch (InstantiationException e) {
74  	    throw new IllegalArgumentException("Unable to construct report renderer class: " + e.getLocalizedMessage());
75  	} catch (IllegalAccessException e) {
76  	    throw new IllegalArgumentException("Unable to construct report renderer class: " + e.getLocalizedMessage());
77  	} catch (InvocationTargetException e) {
78  	    throw new IllegalArgumentException("Unable to construct report renderer class: "
79  		    + e.getTargetException().getLocalizedMessage());
80  	}
81  	// Warn about legacy report format usages
82  	if (REPORT_FORMAT_TO_RENDERER.containsKey(reportFormat) && !reportFormat.equals(renderer.getName())) {
83  	    if (LOG.isLoggable(Level.WARNING)) {
84  	    LOG.warning("Report format '" + reportFormat + "' is deprecated, and has been replaced with '"
85  		    + renderer.getName()
86  		    + "'. Future versions of PMD will remove support for this deprecated Report format usage.");
87  	    }
88  	}
89  	return renderer;
90      }
91  
92      @SuppressWarnings("unchecked")
93      private static Class<? extends Renderer> getRendererClass(String reportFormat) {
94  	Class<? extends Renderer> rendererClass = REPORT_FORMAT_TO_RENDERER.get(reportFormat);
95  
96  	// Look up a custom renderer class
97  	if (rendererClass == null && !"".equals(reportFormat)) {
98  	    try {
99  		Class<?> clazz = Class.forName(reportFormat);
100 		if (!Renderer.class.isAssignableFrom(clazz)) {
101 		    throw new IllegalArgumentException("Custom report renderer class does not implement the "
102 			    + Renderer.class.getName() + " interface.");
103 		} else {
104 		    rendererClass = (Class<? extends Renderer>) clazz;
105 		}
106 	    } catch (ClassNotFoundException e) {
107 		throw new IllegalArgumentException("Can't find the custom format " + reportFormat + ": "
108 			+ e);
109 	    }
110 	}
111 	return rendererClass;
112     }
113 
114     private static Constructor<? extends Renderer> getRendererConstructor(Class<? extends Renderer> rendererClass) {
115 	Constructor<? extends Renderer> constructor = null;
116 
117 	// 1) Properties constructor? - deprecated
118 	try {
119 	    constructor = rendererClass.getConstructor(Properties.class);
120 	    if (!Modifier.isPublic(constructor.getModifiers())) {
121 		constructor = null;
122 	    }
123 	} catch (NoSuchMethodException e) {
124 	    // Ok
125 	}
126 
127 	// 2) No-arg constructor?
128 	try {
129 	    constructor = rendererClass.getConstructor();
130 	    if (!Modifier.isPublic(constructor.getModifiers())) {
131 		constructor = null;
132 	    }
133 	} catch (NoSuchMethodException e2) {
134 	    // Ok
135 	}
136 
137 	if (constructor == null) {
138 	    throw new IllegalArgumentException(
139 		    "Unable to find either a public java.util.Properties or no-arg constructors for Renderer class: "
140 			    + rendererClass.getName());
141 	}
142 	return constructor;
143     }
144 }