1 /*
2  * JasperReports - Free Java Reporting Library.
3  * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
4  * http://www.jaspersoft.com
5  *
6  * Unless you have purchased a commercial license agreement from Jaspersoft,
7  * the following license terms apply:
8  *
9  * This program is part of JasperReports.
10  *
11  * JasperReports is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * JasperReports is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
23  */

24 package net.sf.jasperreports.engine;
25
26 import java.math.BigDecimal;
27 import java.math.BigInteger;
28 import java.net.URLStreamHandlerFactory;
29 import java.text.DateFormat;
30 import java.text.NumberFormat;
31 import java.text.ParseException;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.LinkedList;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.TimeZone;
38
39 import net.sf.jasperreports.annotations.properties.Property;
40 import net.sf.jasperreports.annotations.properties.PropertyScope;
41 import net.sf.jasperreports.engine.export.DefaultHyperlinkProducerFactory;
42 import net.sf.jasperreports.engine.export.ExporterFilter;
43 import net.sf.jasperreports.engine.export.ExporterFilterFactory;
44 import net.sf.jasperreports.engine.export.ExporterFilterFactoryUtil;
45 import net.sf.jasperreports.engine.export.JRExporterContext;
46 import net.sf.jasperreports.engine.export.JRHyperlinkProducer;
47 import net.sf.jasperreports.engine.export.JRHyperlinkProducerFactory;
48 import net.sf.jasperreports.engine.export.data.BooleanTextValue;
49 import net.sf.jasperreports.engine.export.data.DateTextValue;
50 import net.sf.jasperreports.engine.export.data.NumberTextValue;
51 import net.sf.jasperreports.engine.export.data.StringTextValue;
52 import net.sf.jasperreports.engine.export.data.TextValue;
53 import net.sf.jasperreports.engine.fonts.FontUtil;
54 import net.sf.jasperreports.engine.util.DefaultFormatFactory;
55 import net.sf.jasperreports.engine.util.FormatFactory;
56 import net.sf.jasperreports.engine.util.JRClassLoader;
57 import net.sf.jasperreports.engine.util.JRDataUtils;
58 import net.sf.jasperreports.engine.util.JRStyledText;
59 import net.sf.jasperreports.engine.util.JRStyledTextParser;
60 import net.sf.jasperreports.engine.util.JRStyledTextUtil;
61 import net.sf.jasperreports.engine.util.Pair;
62 import net.sf.jasperreports.export.CompositeExporterConfigurationFactory;
63 import net.sf.jasperreports.export.Exporter;
64 import net.sf.jasperreports.export.ExporterConfiguration;
65 import net.sf.jasperreports.export.ExporterInput;
66 import net.sf.jasperreports.export.ExporterInputItem;
67 import net.sf.jasperreports.export.ExporterOutput;
68 import net.sf.jasperreports.export.PropertiesDefaultsConfigurationFactory;
69 import net.sf.jasperreports.export.PropertiesNoDefaultsConfigurationFactory;
70 import net.sf.jasperreports.export.ReportExportConfiguration;
71 import net.sf.jasperreports.properties.PropertyConstants;
72 import net.sf.jasperreports.renderers.util.RendererUtil;
73 import net.sf.jasperreports.repo.RepositoryResourceContext;
74 import net.sf.jasperreports.repo.RepositoryUtil;
75 import net.sf.jasperreports.repo.SimpleRepositoryContext;
76
77
78 /**
79  * @author Teodor Danciu (teodord@users.sourceforge.net)
80  */

81 public abstract class JRAbstractExporter<RC extends ReportExportConfiguration, C extends ExporterConfiguration, O extends ExporterOutput, E extends JRExporterContext> implements JRExporter<ExporterInput, RC, C, O>
82 {
83     public static final String EXCEPTION_MESSAGE_KEY_START_PAGE_INDEX_OUT_OF_RANGE = "export.common.start.page.index.out.of.range";
84     public static final String EXCEPTION_MESSAGE_KEY_END_PAGE_INDEX_OUT_OF_RANGE = "export.common.end.page.index.out.of.range";
85     public static final String EXCEPTION_MESSAGE_KEY_INVALID_IMAGE_NAME = "export.common.invalid.image.name";
86     public static final String EXCEPTION_MESSAGE_KEY_INVALID_ZOOM_RATIO = "export.common.invalid.zoom.ratio";
87     public static final String EXCEPTION_MESSAGE_KEY_MIXED_CALLS_NOT_ALLOWED = "export.common.mixed.calls.not.allowed";
88     public static final String EXCEPTION_MESSAGE_KEY_PAGE_INDEX_OUT_OF_RANGE = "export.common.page.index.out.of.range";
89     public static final String EXCEPTION_MESSAGE_KEY_OUTPUT_WRITER_ERROR = "export.common.output.writer.error";
90
91     /**
92      * The suffix applied to properties that give the default filter factory for
93      * a specific exporter.
94      * 
95      * For instance, the default filter factory for XLS exporters is
96      * <code>net.sf.jasperreports.export.xls.default.filter.factory</code>.
97      * 
98      * If this property is not defined for a specific exporter, the generic
99      * exporter factory given by {@link #PROPERTY_DEFAULT_FILTER_FACTORY} is used.
100      */

101     @Property(
102             name = "net.sf.jasperreports.export.{arbitrary_name}.default.filter.factory",
103             valueType = Class.class,
104             category = PropertyConstants.CATEGORY_EXPORT,
105             defaultValue = "same default value as for net.sf.jasperreports.export.default.filter.factory",
106             scopes = {PropertyScope.CONTEXT},
107             sinceVersion = PropertyConstants.VERSION_3_0_1
108             )
109     public static final String PROPERTY_SUFFIX_DEFAULT_FILTER_FACTORY = "default.filter.factory";
110
111     /**
112      * A property that gives the generic default filter factory class name.
113      * 
114      * @see #PROPERTY_SUFFIX_DEFAULT_FILTER_FACTORY
115      */

116     @Property(
117             category = PropertyConstants.CATEGORY_EXPORT,
118             valueType = Class.class,
119             defaultValue = "net.sf.jasperreports.engine.export.DefaultExporterFilterFactory",
120             scopes = {PropertyScope.CONTEXT, PropertyScope.REPORT},
121             sinceVersion = PropertyConstants.VERSION_3_0_1
122             )
123     public static final String PROPERTY_DEFAULT_FILTER_FACTORY = 
124         JRPropertiesUtil.PROPERTY_PREFIX + "export." + PROPERTY_SUFFIX_DEFAULT_FILTER_FACTORY;
125     
126     public abstract class BaseExporterContext implements JRExporterContext
127     {
128         private Map<String, Object> values = new HashMap<String, Object>();
129
130         /**
131          * @deprecated Replaced by {@link #getExporterRef()}.
132          */

133         @Override
134         public JRExporter getExporter()
135         {
136             return JRAbstractExporter.this;
137         }
138
139         @Override
140         public Exporter getExporterRef()
141         {
142             return JRAbstractExporter.this;
143         }
144
145         @Override
146         public JasperReportsContext getJasperReportsContext()
147         {
148             return JRAbstractExporter.this.getJasperReportsContext();
149         }
150         
151         @Override
152         public RepositoryUtil getRepository()
153         {
154             return JRAbstractExporter.this.getRepository();
155         }
156         
157         @Override
158         public JasperPrint getExportedReport()
159         {
160             return jasperPrint;
161         }
162
163         @Override
164         @SuppressWarnings("deprecation")
165         public Map<JRExporterParameter,Object> getExportParameters()
166         {
167             return parameters;
168         }
169
170         @Override
171         public int getOffsetX()
172         {
173             return JRAbstractExporter.this.getOffsetX();
174         }
175
176         @Override
177         public int getOffsetY()
178         {
179             return JRAbstractExporter.this.getOffsetY();
180         }
181
182         @Override
183         @SuppressWarnings("deprecation")
184         public String getExportPropertiesPrefix()
185         {
186             return JRAbstractExporter.this.getExporterPropertiesPrefix();
187         }
188         
189         @Override
190         public Object getValue(String key)
191         {
192             return values.get(key);
193         }
194         
195         @Override
196         public void setValue(String key, Object value)
197         {
198             values.put(key, value);
199         }
200
201         @Override
202         public Map<String, Object> getValues()
203         {
204             return values;
205         }
206     }
207     
208     // this would make the applet require logging library
209     //private final static Log log = LogFactory.getLog(JRAbstractExporter.class);
210     
211     private Boolean useOldApi = null;
212
213     /**
214      *
215      */

216     protected JasperReportsContext jasperReportsContext;
217     protected JRPropertiesUtil propertiesUtil;
218     protected RendererUtil rendererUtil;
219     protected JRStyledTextAttributeSelector allSelector;
220     protected JRStyledTextAttributeSelector noBackcolorSelector;
221     protected JRStyledTextAttributeSelector noneSelector;
222     protected JRStyledTextUtil styledTextUtil;
223     protected FontUtil fontUtil;
224     
225     /**
226      *
227      */

228     @SuppressWarnings("deprecation")
229     protected Map<JRExporterParameter,Object> parameters = new HashMap<JRExporterParameter,Object>();
230
231     /**
232      *
233      */

234     protected ExporterInput exporterInput;
235     protected RC itemConfiguration;
236     protected C exporterConfiguration;
237     protected O exporterOutput;
238
239     protected ExporterInputItem crtItem;
240     protected RC crtCompositeItemConfiguration;
241     protected C crtCompositeConfiguration;
242     protected JasperPrint jasperPrint;
243
244     /**
245      *
246      */

247     protected ExporterFilter filter;
248
249     /**
250      *
251      */

252     private LinkedList<int[]> elementOffsetStack = new LinkedList<int[]>();
253     private int elementOffsetX;
254     private int elementOffsetY;
255
256     /**
257      *
258      */

259     protected Map<String, DateFormat> dateFormatCache = new HashMap<String, DateFormat>();
260     protected Map<String, NumberFormat> numberFormatCache = new HashMap<String, NumberFormat>();
261
262     /*
263      * cached text locale, JRDataUtils.getLocale(String) is rather slow.
264      * helps in cases where there's a single report locale, which are most likely 9x% of all cases.
265      * note that we're assuming single threaded exporting, otherwise we would need volatile.
266      */

267     private Pair<String, Locale> lastTextLocale;
268     
269     /*
270      * cache of text value class to avoid calling JRClassLoader.loadClassForRealName() each time.
271      * note that we're assuming single threaded exporting.
272      */

273     protected Map<String, Class<?>> textValueClasses = new HashMap<String, Class<?>>();
274     
275     /**
276      *
277      */

278     private ReportContext reportContext;
279     protected E exporterContext;
280
281
282     /**
283      *
284      */

285     protected JRHyperlinkProducerFactory hyperlinkProducerFactory;
286
287     /**
288      *
289      */

290     protected JRAbstractExporter(JasperReportsContext jasperReportsContext)
291     {
292         setJasperReportsContext(jasperReportsContext);
293     }
294     
295     
296     /**
297      * 
298      */

299     private void checkApi(boolean isOldApi)
300     {
301         if (useOldApi == null)
302         {
303             useOldApi = isOldApi;
304         }
305         else
306         {
307             if (useOldApi != isOldApi)
308             {
309                 throw 
310                     new JRRuntimeException(
311                         EXCEPTION_MESSAGE_KEY_MIXED_CALLS_NOT_ALLOWED,  
312                         (Object[])null 
313                         );
314             }
315         }
316     }
317     
318     
319     /**
320      *
321      */

322     public void reset()
323     {
324         useOldApi = null;
325
326         @SuppressWarnings("deprecation")
327         Map<JRExporterParameter,Object> dep = new HashMap<JRExporterParameter,Object>();
328         parameters = dep;
329         
330         elementOffsetStack = new LinkedList<int[]>();
331         exporterInput = null;
332         exporterOutput = null;
333         exporterConfiguration = null;
334         itemConfiguration = null;
335     }
336     
337     
338     /**
339      * @deprecated Replaced by {@link #setExporterInput(ExporterInput)}, {@link #setConfiguration(ExporterConfiguration)},
340      * {@link #setConfiguration(ReportExportConfiguration)} and {@link #setExporterOutput(ExporterOutput)}
341      */

342     @Override
343     public void setParameter(JRExporterParameter parameter, Object value)
344     {
345         checkApi(true);
346         
347         parameters.put(parameter, value);
348         exporterInput = null;
349         exporterOutput = null;
350         exporterConfiguration = null;
351     }
352
353
354     /**
355      * @deprecated Replaced by {@link #setExporterInput(ExporterInput)}, {@link #setConfiguration(ExporterConfiguration)},
356      * {@link #setConfiguration(ReportExportConfiguration)} and {@link #setExporterOutput(ExporterOutput)}.
357      */

358     @Override
359     public Object getParameter(JRExporterParameter parameter)
360     {
361         return parameters.get(parameter);
362     }
363
364
365     /**
366      * @deprecated Replaced by {@link #setExporterInput(ExporterInput)}, {@link #setConfiguration(ExporterConfiguration)},
367      * {@link #setConfiguration(ReportExportConfiguration)} and {@link #setExporterOutput(ExporterOutput)}
368      */

369     @Override
370     public void setParameters(Map<JRExporterParameter,Object> parameters)
371     {
372         checkApi(true);
373
374         this.parameters = parameters;
375         exporterInput = null;
376         exporterOutput = null;
377         exporterConfiguration = null;
378     }
379     
380
381     /**
382      * @deprecated Replaced by {@link #setExporterInput(ExporterInput)}, {@link #setConfiguration(ExporterConfiguration)},
383      * {@link #setConfiguration(ReportExportConfiguration)} and {@link #setExporterOutput(ExporterOutput)}
384      */

385     @Override
386     public Map<JRExporterParameter,Object> getParameters()
387     {
388         return parameters;
389     }
390
391     /**
392      *
393      */

394     protected ExporterInput getExporterInput()
395     {
396         return exporterInput;
397     }
398
399     
400     @Override
401     public void setExporterInput(ExporterInput exporterInput)
402     {
403         checkApi(false);
404
405         this.exporterInput = exporterInput;
406     }
407
408     
409     /**
410      *
411      */

412     public O getExporterOutput()
413     {
414         return exporterOutput;
415     }
416
417     
418     @Override
419     public void setExporterOutput(O exporterOutput)
420     {
421         checkApi(false);
422
423         this.exporterOutput = exporterOutput;
424     }
425
426     
427     @Override
428     public void setConfiguration(RC configuration)
429     {
430         checkApi(false);
431         
432         this.itemConfiguration = configuration;
433     }
434
435     
436     @Override
437     public void setConfiguration(C configuration)
438     {
439         checkApi(false);
440         
441         this.exporterConfiguration = configuration;
442     }
443
444     
445     /**
446      *
447      */

448     public JasperReportsContext getJasperReportsContext()
449     {
450         return jasperReportsContext;
451     }
452
453     
454     /**
455      *
456      */

457     protected void setJasperReportsContext(JasperReportsContext jasperReportsContext)
458     {
459         this.jasperReportsContext = jasperReportsContext;
460         this.propertiesUtil = JRPropertiesUtil.getInstance(jasperReportsContext);
461         this.rendererUtil = RendererUtil.getInstance(jasperReportsContext);
462         this.allSelector = JRStyledTextAttributeSelector.getAllSelector(jasperReportsContext);
463         this.noBackcolorSelector = JRStyledTextAttributeSelector.getNoBackcolorSelector(jasperReportsContext);
464         this.noneSelector = JRStyledTextAttributeSelector.getNoneSelector(jasperReportsContext);
465         this.styledTextUtil = JRStyledTextUtil.getInstance(jasperReportsContext);
466         this.fontUtil = FontUtil.getInstance(jasperReportsContext);
467     }
468
469     
470     @Override
471     public void setReportContext(ReportContext reportContext)
472     {
473         this.reportContext = reportContext;
474     }
475
476     
477     @Override
478     public ReportContext getReportContext()
479     {
480         return reportContext;
481     }
482
483     
484     /**
485      *
486      */

487     public JRPropertiesUtil getPropertiesUtil()
488     {
489         return propertiesUtil;
490     }
491
492     
493     /**
494      *
495      */

496     public RendererUtil getRendererUtil()
497     {
498         return rendererUtil;
499     }
500
501     public RepositoryUtil getRepository()
502     {
503         RepositoryResourceContext resourceContext = crtItem == null ? null : crtItem.getRepositoryReportContext();
504         SimpleRepositoryContext repositoryContext = SimpleRepositoryContext.of(getJasperReportsContext(), resourceContext);
505         return RepositoryUtil.getInstance(repositoryContext);
506     }
507     
508     @Override
509     public abstract void exportReport() throws JRException;
510
511
512     protected void setOffset()
513     {
514         setOffset(true);
515     }
516     
517     /**
518      *
519      */

520     protected void setOffset(boolean setElementOffsets)
521     {
522         if (setElementOffsets)
523         {
524             ReportExportConfiguration configuration = getCurrentItemConfiguration();
525             Integer offsetX = configuration.getOffsetX();
526             if (offsetX != null)
527             {
528                 elementOffsetX = offsetX;
529             }
530             else
531             {
532                 elementOffsetX = 0;
533             }
534
535             Integer offsetY = configuration.getOffsetY();
536             if (offsetY != null)
537             {
538                 elementOffsetY = offsetY;
539             }
540             else
541             {
542                 elementOffsetY = 0;
543             }
544         }
545     }
546     
547
548     /**
549      *
550      */

551     @SuppressWarnings("deprecation")
552     protected void ensureJasperReportsContext()
553     {
554         if (
555             parameters.containsKey(JRExporterParameter.CLASS_LOADER)
556             || parameters.containsKey(JRExporterParameter.URL_HANDLER_FACTORY)
557             )
558         {
559             net.sf.jasperreports.engine.util.LocalJasperReportsContext localJasperReportsContext = 
560                 new net.sf.jasperreports.engine.util.LocalJasperReportsContext(jasperReportsContext);
561
562             if (parameters.containsKey(JRExporterParameter.CLASS_LOADER))
563             {
564                 localJasperReportsContext.setClassLoader((ClassLoader)parameters.get(JRExporterParameter.CLASS_LOADER));
565             }
566
567             if (parameters.containsKey(JRExporterParameter.URL_HANDLER_FACTORY))
568             {
569                 localJasperReportsContext.setURLStreamHandlerFactory((URLStreamHandlerFactory)parameters.get(JRExporterParameter.URL_HANDLER_FACTORY));
570             }
571
572             setJasperReportsContext(localJasperReportsContext);
573         }
574         
575         FontUtil.getInstance(jasperReportsContext).resetThreadMissingFontsCache();
576     }
577         
578
579     /**
580      *
581      */

582     protected void resetExportContext()
583     {
584     }
585
586     
587     /**
588      * @deprecated replaced by {@link #ensureJasperReportsContext() setExportContext} 
589      */

590     protected void setClassLoader()
591     {
592         ensureJasperReportsContext();
593     }
594
595     
596     /**
597      * @deprecated replaced by {@link #resetExportContext() resetExportContext} 
598      */

599     protected void resetClassLoader()
600     {
601         resetExportContext();
602     }
603
604
605     /**
606      *
607      */

608     protected void setCurrentExporterInputItem(ExporterInputItem crtItem)
609     {
610         this.crtItem = crtItem;
611
612         jasperPrint = crtItem.getJasperPrint();
613
614         crtCompositeItemConfiguration = null;
615         
616         initReport();
617     }
618
619
620     /**
621      *
622      */

623     protected RC getCurrentItemConfiguration()
624     {
625         if (crtCompositeItemConfiguration == null)
626         {
627             RC crtItemConfiguration = (RC)crtItem.getConfiguration();
628             
629             if (crtItemConfiguration != null)
630             {
631                 checkApi(false);
632             }
633             
634             if (useOldApi)
635             {
636                 @SuppressWarnings("deprecation")
637                 RC depConf = 
638                     new net.sf.jasperreports.export.parameters.ParametersExporterConfigurationFactory<RC>(
639                         getJasperReportsContext(),
640                         getParameters(),
641                         getCurrentJasperPrint()
642                         ).getConfiguration(
643                             getItemConfigurationInterface()
644                             );
645                 crtCompositeItemConfiguration = depConf; 
646             }
647             else
648             {
649                 PropertiesDefaultsConfigurationFactory<RC> defaultsFactory = new PropertiesDefaultsConfigurationFactory<RC>(jasperReportsContext);
650                 RC defaultsConfiguration = defaultsFactory.getConfiguration(getItemConfigurationInterface());
651                 
652                 PropertiesNoDefaultsConfigurationFactory<RC> noDefaultsFactory = new PropertiesNoDefaultsConfigurationFactory<RC>(jasperReportsContext);
653                 RC noDefaultsConfiguration = noDefaultsFactory.getConfiguration(getItemConfigurationInterface(), getCurrentJasperPrint());
654
655                 CompositeExporterConfigurationFactory<RC> compositeFactory = new CompositeExporterConfigurationFactory<RC>(jasperReportsContext, getItemConfigurationInterface());
656
657                 RC tmpItemConfiguration = compositeFactory.getConfiguration(crtItemConfiguration, noDefaultsConfiguration);
658                 
659                 tmpItemConfiguration = compositeFactory.getConfiguration(itemConfiguration, tmpItemConfiguration);
660                 
661                 crtCompositeItemConfiguration = compositeFactory.getConfiguration(tmpItemConfiguration, defaultsConfiguration, true);
662
663             }
664         }
665         return crtCompositeItemConfiguration;
666     }
667     
668     
669     /**
670      *
671      */

672     protected C getCurrentConfiguration()
673     {
674         if (crtCompositeConfiguration == null)
675         {
676             if (useOldApi)
677             {
678                 @SuppressWarnings("deprecation")
679                 C depConf = 
680                     new net.sf.jasperreports.export.parameters.ParametersExporterConfigurationFactory<C>(
681                         getJasperReportsContext(),
682                         getParameters(),
683                         getCurrentJasperPrint()
684                         ).getConfiguration(
685                             getConfigurationInterface()
686                             );
687                 crtCompositeConfiguration = depConf;
688             }
689             else
690             {
691                 PropertiesDefaultsConfigurationFactory<C> defaultsFactory = new PropertiesDefaultsConfigurationFactory<C>(jasperReportsContext);
692                 C defaultsConfiguration = defaultsFactory.getConfiguration(getConfigurationInterface());
693
694                 PropertiesNoDefaultsConfigurationFactory<C> noDefaultsFactory = new PropertiesNoDefaultsConfigurationFactory<C>(jasperReportsContext);
695                 C noDefaultsConfiguration = noDefaultsFactory.getConfiguration(getConfigurationInterface(), getCurrentJasperPrint());
696
697                 CompositeExporterConfigurationFactory<C> compositeFactory = new CompositeExporterConfigurationFactory<C>(jasperReportsContext, getConfigurationInterface());
698
699                 C tmpItemConfiguration = compositeFactory.getConfiguration(exporterConfiguration, noDefaultsConfiguration);
700                 
701                 crtCompositeConfiguration = compositeFactory.getConfiguration(tmpItemConfiguration, defaultsConfiguration, true);
702             }
703
704         }
705         return crtCompositeConfiguration;
706     }
707     
708     
709     /**
710      *
711      */

712     protected abstract Class<C> getConfigurationInterface();
713
714     
715     /**
716      *
717      */

718     protected abstract Class<RC> getItemConfigurationInterface();
719
720     
721     /**
722      *
723      */

724     @SuppressWarnings("deprecation")
725     protected void ensureInput()
726     {
727         if (exporterInput == null)
728         {
729             exporterInput = new net.sf.jasperreports.export.parameters.ParametersExporterInput(parameters);
730         }
731         
732         crtItem = exporterInput.getItems().get(0);//for getRepository
733         jasperPrint = crtItem.getJasperPrint();//this is just for the sake of getCurrentConfiguration() calls made prior to any setCurrentExporterInputItem() call
734     }
735
736     
737     /**
738      *
739      */

740     protected abstract void ensureOutput();
741     
742
743     /**
744      *
745      */

746     protected void initExport()
747     {
748         crtCompositeConfiguration = null;
749     }
750     
751
752     
753
754     /**
755      *
756      */

757     protected void initReport()
758     {
759         JRStyledTextParser.setLocale(getLocale());
760
761         setOffset();
762         
763         filter = getCurrentItemConfiguration().getExporterFilter();
764         if (filter == null)
765         {
766             filter = createFilter();
767         }
768     }
769     
770
771     /**
772      *
773      */

774     protected PageRange getPageRange()
775     {
776         Integer startPageIndex = null;
777         Integer endPageIndex = null;
778         
779         int lastPageIndex = -1;
780         if (jasperPrint.getPages() != null)
781         {
782             lastPageIndex = jasperPrint.getPages().size() - 1;
783         }
784
785         ReportExportConfiguration configuration = getCurrentItemConfiguration();
786         
787         Integer start = configuration.getStartPageIndex();
788         if (start != null)
789         {
790             startPageIndex = start;
791             if (startPageIndex < 0 || startPageIndex > lastPageIndex)
792             {
793                 throw 
794                     new JRRuntimeException(
795                         EXCEPTION_MESSAGE_KEY_START_PAGE_INDEX_OUT_OF_RANGE,  
796                         new Object[]{startPageIndex, lastPageIndex} 
797                         );
798             }
799         }
800
801         Integer end = configuration.getEndPageIndex();
802         if (end != null)
803         {
804             endPageIndex = end;
805             int startPage = startPageIndex == null ? 0 : startPageIndex;
806             if (endPageIndex < startPage || endPageIndex > lastPageIndex)
807             {
808                 throw 
809                     new JRRuntimeException(
810                         EXCEPTION_MESSAGE_KEY_END_PAGE_INDEX_OUT_OF_RANGE,  
811                         new Object[]{startPage, endPageIndex, lastPageIndex} 
812                         );
813             }
814         }
815
816         Integer pageIndex = configuration.getPageIndex();
817         if (pageIndex != null)
818         {
819             if (pageIndex < 0 || pageIndex > lastPageIndex)
820             {
821                 throw 
822                     new JRRuntimeException(
823                         EXCEPTION_MESSAGE_KEY_PAGE_INDEX_OUT_OF_RANGE,  
824                         new Object[]{pageIndex, lastPageIndex}
825                         );
826             }
827             startPageIndex = pageIndex;
828             endPageIndex = pageIndex;
829         }
830         
831         PageRange pageRange = null;
832         
833         if (startPageIndex != null || endPageIndex != null)
834         {
835             pageRange = new PageRange(startPageIndex, endPageIndex);
836         }
837         
838         return pageRange;
839     }
840     
841
842     /**
843      *
844      */

845     protected JRStyledText getStyledText(JRPrintText textElement, boolean setBackcolor)
846     {
847         return styledTextUtil.getStyledText(textElement, setBackcolor ? allSelector : noBackcolorSelector);
848     }
849
850     
851     protected JRStyledText getStyledText(JRPrintText textElement)
852     {
853         return getStyledText(textElement, true);
854     }
855
856
857     /**
858      * Returns the X axis offset used for element export.
859      * 
860      * @return the X axis offset
861      */

862     protected int getOffsetX()
863     {
864         return elementOffsetX;
865     }
866
867
868     /**
869      * Returns the Y axis offset used for element export.
870      * 
871      * @return the Y axis offset
872      */

873     protected int getOffsetY()
874     {
875         return elementOffsetY;
876     }
877
878     
879     /**
880      * Sets the offsets for exporting elements from a {@link JRPrintFrame frame}.
881      * <p>
882      * After the frame elements are exported, a call to {@link #restoreElementOffsets() popElementOffsets} is required
883      * so that the previous offsets are restored.
884      * 
885      * @param frame
886      * @param relative
887      * @see #getOffsetX()
888      * @see #getOffsetY()
889      * @see #restoreElementOffsets()
890      */

891     protected void setFrameElementsOffset(JRPrintFrame frame, boolean relative)
892     {    
893         if (relative)
894         {
895             setElementOffsets(0, 0);
896         }
897         else
898         {
899             int topPadding = frame.getLineBox().getTopPadding();
900             int leftPadding = frame.getLineBox().getLeftPadding();
901
902             setElementOffsets(getOffsetX() + frame.getX() + leftPadding, getOffsetY() + frame.getY() + topPadding);
903         }
904     }
905     
906     
907     private void setElementOffsets(int offsetX, int offsetY)
908     {
909         elementOffsetStack.addLast(new int[]{elementOffsetX, elementOffsetY});
910         
911         elementOffsetX = offsetX;
912         elementOffsetY = offsetY;
913     }
914
915     
916     /**
917      * Restores offsets after a call to 
918      * {@link #setFrameElementsOffset(JRPrintFrame, boolean) setFrameElementsOffset}.
919      */

920     protected void restoreElementOffsets()
921     {
922         int[] offsets = elementOffsetStack.removeLast();
923         elementOffsetX = offsets[0];
924         elementOffsetY = offsets[1];
925     }
926
927     
928     /**
929      *
930      */

931     protected boolean insideFrame()
932     {
933         return elementOffsetStack != null && elementOffsetStack.size() > 0;
934     }
935
936     
937     protected String getTextFormatFactoryClass(JRPrintText text)
938     {
939         String formatFactoryClass = text.getFormatFactoryClass();
940         if (formatFactoryClass == null)
941         {
942             formatFactoryClass = jasperPrint.getFormatFactoryClass();
943         }
944         return formatFactoryClass;
945     }
946
947     public Locale getLocale()
948     {
949         String localeCode = jasperPrint.getLocaleCode();
950         return localeCode == null ? null : JRDataUtils.getLocale(localeCode);
951     }
952
953     protected Locale getTextLocale(JRPrintText text)
954     {
955         String localeCode = text.getLocaleCode();
956         if (localeCode == null)
957         {
958             localeCode = jasperPrint.getLocaleCode();
959         }
960         
961         if (localeCode == null)
962         {
963             return null;
964         }
965         
966         Pair<String, Locale> last = lastTextLocale;
967         if (last != null && last.first().equals(localeCode))
968         {
969             return last.second();
970         }
971         
972         Locale locale = JRDataUtils.getLocale(localeCode);
973         lastTextLocale = new Pair<String, Locale>(localeCode, locale);
974         return locale;
975     }
976
977     protected TimeZone getTextTimeZone(JRPrintText text)
978     {
979         String tzId = text.getTimeZoneId();
980         if (tzId == null)
981         {
982             tzId = jasperPrint.getTimeZoneId();
983         }
984         return tzId == null ? null : JRDataUtils.getTimeZone(tzId);
985     }
986     
987     protected TextValue getTextValue(JRPrintText text, String textStr)
988     {
989         TextValue textValue;
990         String valueClassName = text.getValueClassName();
991         if (valueClassName == null)
992         {
993             textValue = getTextValueString(text, textStr);
994         }
995         else
996         {
997             try
998             {
999                 Class<?> valueClass = textValueClasses.get(valueClassName);
1000                 if (valueClass == null)
1001                 {
1002                     valueClass = JRClassLoader.loadClassForRealName(valueClassName);
1003                     textValueClasses.put(valueClassName, valueClass);
1004                 }
1005                 
1006                 if (java.lang.Number.class.isAssignableFrom(valueClass))
1007                 {
1008                     textValue = getNumberCellValue(text, textStr);
1009                 }
1010                 else if (Date.class.isAssignableFrom(valueClass))
1011                 {
1012                     textValue = getDateCellValue(text, textStr);
1013                 }
1014                 else if (Boolean.class.equals(valueClass))
1015                 {
1016                     textValue = getBooleanCellValue(text, textStr);
1017                 }
1018                 else
1019                 {
1020                     textValue = getTextValueString(text, textStr);
1021                 } 
1022             }
1023             catch (ParseException e)
1024             {
1025                 //log.warn("Error parsing text value", e);
1026                 textValue = getTextValueString(text, textStr);
1027             }
1028             catch (ClassNotFoundException e)
1029             {
1030                 //log.warn("Error loading text value class", e);
1031                 textValue = getTextValueString(text, textStr);
1032             }            
1033         }
1034         return textValue;
1035     }
1036
1037     protected TextValue getTextValueString(JRPrintText text, String textStr)
1038     {
1039         return new StringTextValue(textStr);
1040     }
1041
1042     protected TextValue getDateCellValue(JRPrintText text, String textStr) throws ParseException
1043     {
1044         if (textStr != null && text.getValue() == null)
1045         {
1046             TextValue textValue;
1047             String pattern = text.getPattern();
1048             if (pattern == null || pattern.trim().length() == 0)//FIXMENOW there might be formatters that do not use pattern, in which case this test would skip them
1049             {
1050                 textValue = getTextValueString(text, textStr);
1051             }
1052             else
1053             {
1054                 DateFormat dateFormat = getDateFormat(getTextFormatFactoryClass(text), pattern, getTextLocale(text), getTextTimeZone(text));
1055                 
1056                 Date value = null;
1057                 if (textStr != null && textStr.length() > 0)
1058                 {
1059                     value = dateFormat.parse(textStr);
1060                 }
1061                 textValue = new DateTextValue(textStr, value, text.getPattern());
1062             }
1063             return textValue;
1064         }
1065         else
1066         {
1067             return new DateTextValue(textStr, (Date)text.getValue(), text.getPattern());
1068         }
1069     }
1070
1071     protected TextValue getNumberCellValue(JRPrintText text, String textStr) throws ParseException, ClassNotFoundException
1072     {
1073         if (textStr != null && text.getValue() == null)
1074         {
1075             TextValue textValue;
1076             String pattern = text.getPattern();
1077             if (pattern == null || pattern.trim().length() == 0)//FIXMENOW there might be formatters that do not use pattern, in which case this test would skip them
1078             {
1079                 if (textStr != null && textStr.length() > 0)
1080                 {
1081                     Number value = defaultParseNumber(textStr, JRClassLoader.loadClassForRealName(text.getValueClassName()));
1082
1083                     if (value != null)
1084                     {
1085                         textValue = new NumberTextValue(textStr, value, text.getPattern());
1086                     }
1087                     else
1088                     {
1089                         textValue = getTextValueString(text, textStr);
1090                     }
1091                 }
1092                 else
1093                 {
1094                     textValue = new NumberTextValue(textStr, null, text.getPattern());
1095                 }
1096             }
1097             else
1098             {
1099                 NumberFormat numberFormat = getNumberFormat(getTextFormatFactoryClass(text), pattern, getTextLocale(text));
1100                 
1101                 Number value = null;
1102                 if (textStr != null && textStr.length() > 0)
1103                 {
1104                     value = numberFormat.parse(textStr);
1105                 }
1106                 textValue = new NumberTextValue(textStr, value, text.getPattern());
1107             }
1108             return textValue;
1109         }
1110         else
1111         {
1112             return new NumberTextValue(textStr, (Number)text.getValue(), text.getPattern());
1113         }
1114     }
1115
1116     protected Number defaultParseNumber(String textStr, Class<?> valueClass)
1117     {
1118         Number value = null;
1119         try
1120         {
1121             if (valueClass.equals(Byte.class))
1122             {
1123                 value = Byte.valueOf(textStr);
1124             }
1125             else if (valueClass.equals(Short.class))
1126             {
1127                 value = Short.valueOf(textStr);
1128             }
1129             else if (valueClass.equals(Integer.class))
1130             {
1131                 value = Integer.valueOf(textStr);
1132             }
1133             else if (valueClass.equals(Long.class))
1134             {
1135                 value = Long.valueOf(textStr);
1136             }
1137             else if (valueClass.equals(Float.class))
1138             {
1139                 value = Float.valueOf(textStr);
1140             }
1141             else if (valueClass.equals(Double.class))
1142             {
1143                 value = Double.valueOf(textStr);
1144             }
1145             else if (valueClass.equals(BigInteger.class))
1146             {
1147                 value = new BigInteger(textStr);
1148             }
1149             else if (valueClass.equals(BigDecimal.class))
1150             {
1151                 value = new BigDecimal(textStr);
1152             }
1153         }
1154         catch (NumberFormatException e)
1155         {
1156             //skip
1157         }
1158         return value;
1159     }
1160     
1161     protected TextValue getBooleanCellValue(JRPrintText text, String textStr)
1162     {
1163         Boolean value = null;
1164         if (textStr != null && textStr.length() > 0)
1165         {
1166             value = Boolean.valueOf(textStr);
1167         }
1168         return new BooleanTextValue(textStr, value);
1169     }
1170
1171     protected DateFormat getDateFormat(String formatFactoryClass, String pattern, Locale lc, TimeZone tz)
1172     {
1173         String key = formatFactoryClass 
1174             + "|" + pattern 
1175             + "|" + (lc == null ? "" : JRDataUtils.getLocaleCode(lc)) 
1176             + "|" + (tz == null ? "" : JRDataUtils.getTimeZoneId(tz));
1177         DateFormat dateFormat = dateFormatCache.get(key);
1178         if (dateFormat == null)
1179         {
1180             FormatFactory formatFactory = DefaultFormatFactory.createFormatFactory(formatFactoryClass);//FIXMEFORMAT cache this too
1181             dateFormat = formatFactory.createDateFormat(pattern, lc, tz);
1182             dateFormatCache.put(key, dateFormat);
1183         }
1184         return dateFormat;
1185     }
1186
1187     protected NumberFormat getNumberFormat(String formatFactoryClass, String pattern, Locale lc)
1188     {
1189         String key = formatFactoryClass 
1190             + "|" + pattern 
1191             + "|" + (lc == null ? "" : JRDataUtils.getLocaleCode(lc)); 
1192         NumberFormat numberFormat = numberFormatCache.get(key);
1193         if (numberFormat == null)
1194         {
1195             FormatFactory formatFactory = DefaultFormatFactory.createFormatFactory(formatFactoryClass);//FIXMEFORMAT cache this too
1196             numberFormat = formatFactory.createNumberFormat(pattern, lc);
1197             numberFormatCache.put(key, numberFormat);
1198         }
1199         return numberFormat;
1200     }
1201     
1202     /**
1203      * 
1204      */

1205     protected ExporterFilter createFilter()
1206     {
1207         String exportDefaultFactoryProperty = getExporterPropertiesPrefix() 
1208                 + PROPERTY_SUFFIX_DEFAULT_FILTER_FACTORY;
1209         
1210         //the default filter class is determined from 4 possible sources
1211         String defaultFilterClassName = null;
1212         
1213         if (jasperPrint.hasProperties())
1214         {
1215             //try first the exporter specific property from the report
1216             defaultFilterClassName = jasperPrint.getPropertiesMap().getProperty(
1217                     exportDefaultFactoryProperty);
1218             
1219             //then the generic property from the report
1220             if (defaultFilterClassName == null)
1221             {
1222                 defaultFilterClassName = jasperPrint.getPropertiesMap().getProperty(
1223                         PROPERTY_DEFAULT_FILTER_FACTORY);
1224             }
1225         }
1226         
1227         //then the global exporter specific property
1228         if (defaultFilterClassName == null)
1229         {
1230             defaultFilterClassName = getPropertiesUtil().getProperty(exportDefaultFactoryProperty);
1231         }
1232         
1233         //and finally the global generic property
1234         if (defaultFilterClassName == null)
1235         {
1236             defaultFilterClassName = getPropertiesUtil().getProperty(PROPERTY_DEFAULT_FILTER_FACTORY);
1237         }
1238         
1239         ExporterFilter filter = null;
1240         
1241         try
1242         {
1243             ExporterFilterFactory defaultFactory = ExporterFilterFactoryUtil.getFilterFactory(defaultFilterClassName);
1244             filter = defaultFactory.getFilter(getExporterContext());
1245         }
1246         catch (JRException e)
1247         {
1248             throw new JRRuntimeException(e);
1249         }
1250
1251         return filter;
1252     }
1253
1254     public JRHyperlinkProducer getHyperlinkProducer(JRPrintHyperlink link)
1255     {
1256         if (hyperlinkProducerFactory == null)
1257         {
1258             hyperlinkProducerFactory = getCurrentItemConfiguration().getHyperlinkProducerFactory();
1259
1260             if (hyperlinkProducerFactory == null)
1261             {
1262                 hyperlinkProducerFactory = new DefaultHyperlinkProducerFactory(jasperReportsContext);
1263             }
1264         }
1265
1266         return hyperlinkProducerFactory.getHandler(link.getLinkType());
1267     }
1268
1269     /**
1270      * 
1271      */

1272     public abstract String getExporterKey();
1273
1274     /**
1275      * Returns the properties prefix for the current exporter.
1276      * 
1277      * @return the properties prefix for the current exporter
1278      */

1279     public abstract String getExporterPropertiesPrefix();
1280
1281     /**
1282      * 
1283      */

1284     public E getExporterContext()
1285     {
1286         return exporterContext;
1287     }
1288
1289     public JasperPrint getCurrentJasperPrint()
1290     {
1291         return jasperPrint;
1292     }
1293
1294     protected class PageRange
1295     {
1296         private Integer startPageIndex;
1297         private Integer endPageIndex;
1298         
1299         /**
1300          * 
1301          */

1302         public PageRange(Integer startPageIndex, Integer endPageIndex)
1303         {
1304             this.startPageIndex = startPageIndex;
1305             this.endPageIndex = endPageIndex;
1306         }
1307         
1308         /**
1309          * 
1310          */

1311         public Integer getStartPageIndex()
1312         {
1313             return startPageIndex;
1314         }
1315
1316         /**
1317          * 
1318          */

1319         public Integer getEndPageIndex()
1320         {
1321             return endPageIndex;
1322         }
1323     }
1324 }
1325