001: /*
002: * Copyright 2002-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.web.servlet.view.jasperreports;
018:
019: import java.util.Enumeration;
020: import java.util.HashMap;
021: import java.util.Map;
022: import java.util.Properties;
023:
024: import javax.servlet.http.HttpServletResponse;
025:
026: import net.sf.jasperreports.engine.JasperPrint;
027:
028: import org.springframework.beans.BeanUtils;
029: import org.springframework.context.ApplicationContextException;
030: import org.springframework.util.ClassUtils;
031:
032: /**
033: * Jasper Reports view class that allows for the actual rendering format to be
034: * specified at runtime using a parameter contained in the model.
035: *
036: * <p>This view works on the concept of a format key and a mapping key.
037: * The format key is used to pass the mapping key from your
038: * <code>Controller</code> to Spring through as part of the model and the
039: * mapping key is used to map a logical format to an actual JasperReports
040: * view class. For example you might add the following code to your
041: * <code>Controller</code>:
042: *
043: * <pre>
044: * Map model = new HashMap();
045: * model.put("format", "pdf");</pre>
046: *
047: * Here <code>format</code> is the format key and <code>pdf</code> is
048: * the mapping key. When rendering a report, this class looks for a
049: * model parameter under the format key, which by default is
050: * <code>format</code>. It then uses the value of this parameter to lookup
051: * the actual <code>View</code> class to use. The default mappings for this
052: * lookup are:
053: *
054: * <p><ul>
055: * <li><code>csv</code> - <code>JasperReportsCsvView</code></li>
056: * <li><code>html</code> - <code>JasperReportsHtmlView</code></li>
057: * <li><code>pdf</code> - <code>JasperReportsPdfView</code></li>
058: * <li><code>xls</code> - <code>JasperReportsXlsView</code></li>
059: * </ul>
060: *
061: * <p>The format key can be changed using the <code>formatKey</code>
062: * property and the mapping key to view class mappings can be changed using the
063: * <code>formatMappings</code> property.
064: *
065: * @author Rob Harrop
066: * @author Juergen Hoeller
067: * @since 1.1.5
068: * @see #setFormatKey
069: * @see #setFormatMappings
070: */
071: public class JasperReportsMultiFormatView extends
072: AbstractJasperReportsView {
073:
074: /**
075: * Default value used for format key: "format"
076: */
077: public static final String DEFAULT_FORMAT_KEY = "format";
078:
079: /**
080: * The key of the model parameter that holds the format key.
081: */
082: private String formatKey = DEFAULT_FORMAT_KEY;
083:
084: /**
085: * Stores the format mappings, with the format discriminator
086: * as key and the corresponding view class as value.
087: */
088: private Map formatMappings;
089:
090: /**
091: * Stores the mappings of mapping keys to Content-Disposition header values.
092: */
093: private Properties contentDispositionMappings;
094:
095: /**
096: * Creates a new <code>JasperReportsMultiFormatView</code> instance
097: * with a default set of mappings.
098: */
099: public JasperReportsMultiFormatView() {
100: this .formatMappings = new HashMap(4);
101: this .formatMappings.put("csv", JasperReportsCsvView.class);
102: this .formatMappings.put("html", JasperReportsHtmlView.class);
103: this .formatMappings.put("pdf", JasperReportsPdfView.class);
104: this .formatMappings.put("xls", JasperReportsXlsView.class);
105: }
106:
107: /**
108: * Set the key of the model parameter that holds the format discriminator.
109: * Default is "format".
110: */
111: public void setFormatKey(String formatKey) {
112: this .formatKey = formatKey;
113: }
114:
115: /**
116: * Set the mappings of format discriminators to view class names.
117: * The default mappings are:
118: * <p><ul>
119: * <li><code>csv</code> - <code>JasperReportsCsvView</code></li>
120: * <li><code>html</code> - <code>JasperReportsHtmlView</code></li>
121: * <li><code>pdf</code> - <code>JasperReportsPdfView</code></li>
122: * <li><code>xls</code> - <code>JasperReportsXlsView</code></li>
123: * </ul>
124: */
125: public void setFormatMappings(Properties mappingsWithClassNames) {
126: if (mappingsWithClassNames == null
127: || mappingsWithClassNames.isEmpty()) {
128: throw new IllegalArgumentException(
129: "formatMappings must not be empty");
130: }
131:
132: this .formatMappings = new HashMap(mappingsWithClassNames.size());
133: for (Enumeration discriminators = mappingsWithClassNames
134: .propertyNames(); discriminators.hasMoreElements();) {
135: String discriminator = (String) discriminators
136: .nextElement();
137: String className = mappingsWithClassNames
138: .getProperty(discriminator);
139: try {
140: if (logger.isDebugEnabled()) {
141: logger.debug("Mapped view class [" + className
142: + "] to mapping key [" + discriminator
143: + "]");
144: }
145: this .formatMappings.put(discriminator, ClassUtils
146: .forName(className));
147: } catch (ClassNotFoundException ex) {
148: throw new ApplicationContextException("Class ["
149: + className + "] mapped to format ["
150: + discriminator + "] cannot be found", ex);
151: }
152: }
153: }
154:
155: /**
156: * Set the mappings of <code>Content-Disposition</code> header values to
157: * mapping keys. If specified, Spring will look at these mappings to determine
158: * the value of the <code>Content-Disposition</code> header for a given
159: * format mapping.
160: */
161: public void setContentDispositionMappings(Properties mappings) {
162: this .contentDispositionMappings = mappings;
163: }
164:
165: /**
166: * Return the mappings of <code>Content-Disposition</code> header values to
167: * mapping keys. Mainly available for configuration through property paths
168: * that specify individual keys.
169: */
170: public Properties getContentDispositionMappings() {
171: if (this .contentDispositionMappings == null) {
172: this .contentDispositionMappings = new Properties();
173: }
174: return this .contentDispositionMappings;
175: }
176:
177: /**
178: * Locates the format key in the model using the configured discriminator key and uses this
179: * key to lookup the appropriate view class from the mappings. The rendering of the
180: * report is then delegated to an instance of that view class.
181: */
182: protected void renderReport(JasperPrint populatedReport, Map model,
183: HttpServletResponse response) throws Exception {
184:
185: String format = (String) model.get(this .formatKey);
186: if (format == null) {
187: throw new IllegalArgumentException(
188: "No format format found in model");
189: }
190:
191: if (logger.isDebugEnabled()) {
192: logger.debug("Rendering report using format mapping key ["
193: + format + "]");
194: }
195:
196: Class viewClass = (Class) this .formatMappings.get(format);
197: if (viewClass == null) {
198: throw new IllegalArgumentException("Format discriminator ["
199: + format + "] is not a configured mapping");
200: }
201:
202: if (logger.isDebugEnabled()) {
203: logger.debug("Rendering report using view class ["
204: + viewClass.getName() + "]");
205: }
206:
207: AbstractJasperReportsView view = (AbstractJasperReportsView) BeanUtils
208: .instantiateClass(viewClass);
209:
210: // Copy appropriate properties across.
211: view.setExporterParameters(getExporterParameters());
212:
213: // Can skip most initialization since all relevant URL processing
214: // has been done - just need to convert parameters on the sub view.
215: view.convertExporterParameters();
216:
217: // Prepare response and render report.
218: populateContentDispositionIfNecessary(response, format);
219: view.renderReport(populatedReport, model, response);
220: }
221:
222: /**
223: * Adds/overwrites the <code>Content-Disposition</code> header value with the format-specific
224: * value if the mappings have been specified and a valid one exists for the given format.
225: * @param response the <code>HttpServletResponse</code> to set the header in
226: * @param format the format key of the mapping
227: * @see #setContentDispositionMappings
228: */
229: private void populateContentDispositionIfNecessary(
230: HttpServletResponse response, String format) {
231: if (this .contentDispositionMappings != null) {
232: String header = this .contentDispositionMappings
233: .getProperty(format);
234: if (header != null) {
235: if (logger.isDebugEnabled()) {
236: logger
237: .debug("Setting Content-Disposition header to: ["
238: + header + "]");
239: }
240: response.setHeader(HEADER_CONTENT_DISPOSITION, header);
241: }
242: }
243: }
244:
245: }
|