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.fill;
25
26 import java.text.DecimalFormat;
27 import java.text.Format;
28 import java.text.SimpleDateFormat;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.TimeZone;
32
33 import org.apache.commons.logging.Log;
34 import org.apache.commons.logging.LogFactory;
35
36 import net.sf.jasperreports.engine.JRException;
37 import net.sf.jasperreports.engine.JRExpression;
38 import net.sf.jasperreports.engine.JRExpressionCollector;
39 import net.sf.jasperreports.engine.JRGroup;
40 import net.sf.jasperreports.engine.JRHyperlinkParameter;
41 import net.sf.jasperreports.engine.JRPrintElement;
42 import net.sf.jasperreports.engine.JRPrintHyperlinkParameters;
43 import net.sf.jasperreports.engine.JRPrintText;
44 import net.sf.jasperreports.engine.JRStyle;
45 import net.sf.jasperreports.engine.JRTextField;
46 import net.sf.jasperreports.engine.JRVisitor;
47 import net.sf.jasperreports.engine.type.EvaluationTimeEnum;
48 import net.sf.jasperreports.engine.type.HyperlinkTypeEnum;
49 import net.sf.jasperreports.engine.type.PositionTypeEnum;
50 import net.sf.jasperreports.engine.type.RotationEnum;
51 import net.sf.jasperreports.engine.type.TextAdjustEnum;
52 import net.sf.jasperreports.engine.util.JRDataUtils;
53 import net.sf.jasperreports.engine.util.Pair;
54
55
56 /**
57  * @author Teodor Danciu (teodord@users.sourceforge.net)
58  */

59 public class JRFillTextField extends JRFillTextElement implements JRTextField
60 {
61     
62     protected static final Log log = LogFactory.getLog(JRFillTextField.class);
63
64     protected final Map<Pair<JRStyle, TextFormat>, JRTemplateElement> textTemplates;
65     
66     /**
67      *
68      */

69     private JRGroup evaluationGroup;
70
71     /**
72      *
73      */

74     private Object value;
75     
76     private TimeZone ownTimeZone;
77
78     /**
79      *
80      */

81     private TextFormat textFormat;
82
83     /**
84      * 
85      */

86     private String pattern;
87
88     /**
89      *
90      */

91     private String anchorName;
92     private Integer bookmarkLevel;
93     private String hyperlinkReference;
94     private Boolean hyperlinkWhen;
95     private String hyperlinkAnchor;
96     private Integer hyperlinkPage;
97     private String hyperlinkTooltip;
98     private JRPrintHyperlinkParameters hyperlinkParameters;
99     
100     private static final String NULL_VALUE = new String();
101     private final Map<String, String> localizedProperties;
102     
103     //FIXME keep these in the filler/context
104     private Map<String, TimeZone> generalPatternTimeZones = new HashMap<String, TimeZone>();
105
106     /**
107      *
108      */

109     protected JRFillTextField(
110         JRBaseFiller filler,
111         JRTextField textField, 
112         JRFillObjectFactory factory
113         )
114     {
115         super(filler, textField, factory);
116         
117         this.textTemplates = new HashMap<Pair<JRStyle,TextFormat>, JRTemplateElement>();
118         evaluationGroup = factory.getGroup(textField.getEvaluationGroup());
119         this.localizedProperties = new HashMap<String, String>();
120     }
121
122     
123     protected JRFillTextField(JRFillTextField textField, JRFillCloneFactory factory)
124     {
125         super(textField, factory);
126
127         this.textTemplates = textField.textTemplates;
128         this.evaluationGroup = textField.evaluationGroup;
129         this.localizedProperties = textField.localizedProperties;
130     }
131
132
133     /**
134      * @deprecated Replaced by {@link #getTextAdjust()}.
135      */

136     @Override
137     public boolean isStretchWithOverflow()
138     {
139         return getTextAdjust() == TextAdjustEnum.STRETCH_HEIGHT;
140     }
141
142     /**
143      * @deprecated Replaced by {@link #setTextAdjust(TextAdjustEnum)}.
144      */

145     @Override
146     public void setStretchWithOverflow(boolean isStretchWithOverflow)
147     {
148     }
149
150     @Override
151     public TextAdjustEnum getTextAdjust()
152     {
153         return ((JRTextField)parent).getTextAdjust();
154     }
155
156     @Override
157     public void setTextAdjust(TextAdjustEnum textAdjust)
158     {
159     }
160
161     @Override
162     public EvaluationTimeEnum getEvaluationTimeValue()
163     {
164         return ((JRTextField)parent).getEvaluationTimeValue();
165     }
166         
167     /**
168      *
169      */

170     protected TextFormat getTextFormat()
171     {
172         return textFormat;
173     }
174
175     @Override
176     public String getPattern()
177     {
178         if (getPatternExpression() == null)
179         {
180             return getStyleResolver().getPattern(this);
181         }
182         return pattern;
183     }
184     
185     protected String getDatePattern(Object value)
186     {
187         String pattern = getPattern();
188         if (pattern != null || value == null)
189         {
190             return pattern;
191         }
192         
193         String property;
194         if (value instanceof java.sql.Date)
195         {
196             property = JRTextField.PROPERTY_PATTERN_DATE;
197         }
198         else if (value instanceof java.sql.Time)
199         {
200             property = JRTextField.PROPERTY_PATTERN_TIME;
201         }
202         else 
203         {
204             property = JRTextField.PROPERTY_PATTERN_DATETIME;
205         }
206         return getLocalizedProperty(property);
207     }
208     
209     protected String getNumberPattern(Object value)
210     {
211         String pattern = getPattern();
212         if (pattern != null || value == null)
213         {
214             return pattern;
215         }
216         
217         String property;
218         if (value instanceof java.lang.Byte
219                 || value instanceof java.lang.Short
220                 || value instanceof java.lang.Integer
221                 || value instanceof java.lang.Long
222                 || value instanceof java.math.BigInteger)
223         {
224             property = JRTextField.PROPERTY_PATTERN_INTEGER;
225         }
226         else 
227         {
228             property = JRTextField.PROPERTY_PATTERN_NUMBER;
229         }
230         return getLocalizedProperty(property);
231     }
232     
233     protected String getLocalizedProperty(String property)
234     {
235         // caching locally because it's not cached in JRPropertiesUtil
236         String value = localizedProperties.get(property);
237         if (value == null)
238         {
239             value = filler.getPropertiesUtil().getLocalizedProperty(property, filler.getLocale());
240             localizedProperties.put(property, value == null ? NULL_VALUE : value);
241         }
242         else if (value == NULL_VALUE)
243         {
244             value = null;
245         }
246         return value;
247     }
248         
249     @Override
250     public String getOwnPattern()
251     {
252         return providerStyle == null || providerStyle.getOwnPattern() == null ? ((JRTextField)this.parent).getOwnPattern() : providerStyle.getOwnPattern();
253     }
254
255     @Override
256     public void setPattern(String pattern)
257     {
258     }
259         
260     @Override
261     public boolean isBlankWhenNull()
262     {
263         return getStyleResolver().isBlankWhenNull(this);
264     }
265
266     @Override
267     public Boolean isOwnBlankWhenNull()
268     {
269         return providerStyle == null || providerStyle.isOwnBlankWhenNull() == null ? ((JRTextField)this.parent).isOwnBlankWhenNull() : providerStyle.isOwnBlankWhenNull();
270     }
271
272     @Override
273     public void setBlankWhenNull(boolean isBlank)
274     {
275     }
276
277     @Override
278     public void setBlankWhenNull(Boolean isBlank)
279     {
280     }
281
282     /**
283      * @deprecated Replaced by {@link #getHyperlinkTypeValue()}.
284      */

285     public byte getHyperlinkType()
286     {
287         return getHyperlinkTypeValue().getValue();
288     }
289
290     @Override
291     public HyperlinkTypeEnum getHyperlinkTypeValue()
292     {
293         return ((JRTextField)parent).getHyperlinkTypeValue();
294     }
295         
296     @Override
297     public byte getHyperlinkTarget()
298     {
299         return ((JRTextField)parent).getHyperlinkTarget();
300     }
301         
302     @Override
303     public String getLinkTarget()
304     {
305         return ((JRTextField)parent).getLinkTarget();
306     }
307         
308     @Override
309     public JRGroup getEvaluationGroup()
310     {
311         return evaluationGroup;
312     }
313         
314     @Override
315     public JRExpression getExpression()
316     {
317         return ((JRTextField)parent).getExpression();
318     }
319
320     @Override
321     public JRExpression getPatternExpression()
322     {
323         return ((JRTextField)parent).getPatternExpression();
324     }
325     
326     @Override
327     public JRExpression getBookmarkLevelExpression()
328     {
329         return ((JRTextField)parent).getBookmarkLevelExpression();
330     }
331     
332
333     @Override
334     public JRExpression getAnchorNameExpression()
335     {
336         return ((JRTextField)parent).getAnchorNameExpression();
337     }
338
339     @Override
340     public JRExpression getHyperlinkReferenceExpression()
341     {
342         return ((JRTextField)parent).getHyperlinkReferenceExpression();
343     }
344
345     @Override
346     public JRExpression getHyperlinkWhenExpression()
347     {
348         return ((JRTextField)parent).getHyperlinkWhenExpression();
349     }
350
351     @Override
352     public JRExpression getHyperlinkAnchorExpression()
353     {
354         return ((JRTextField)parent).getHyperlinkAnchorExpression();
355     }
356
357     @Override
358     public JRExpression getHyperlinkPageExpression()
359     {
360         return ((JRTextField)parent).getHyperlinkPageExpression();
361     }
362
363         
364     /**
365      *
366      */

367     protected Object getValue()
368     {
369         return value;
370     }
371
372     /**
373      *
374      */

375     protected String getAnchorName()
376     {
377         return anchorName;
378     }
379
380     /**
381      *
382      */

383     protected String getHyperlinkReference()
384     {
385         return hyperlinkReference;
386     }
387
388     /**
389      *
390      */

391     protected String getHyperlinkAnchor()
392     {
393         return hyperlinkAnchor;
394     }
395
396     /**
397      *
398      */

399     protected Integer getHyperlinkPage()
400     {
401         return hyperlinkPage;
402     }
403         
404
405     protected String getHyperlinkTooltip()
406     {
407         return hyperlinkTooltip;
408     }
409         
410
411     /**
412      *
413      */

414     protected JRTemplateText getJRTemplateText()
415     {
416         return (JRTemplateText) getElementTemplate();
417     }
418
419
420     @Override
421     protected JRTemplateElement createElementTemplate()
422     {
423         JRTemplateText template = 
424             new JRTemplateText(
425                 getElementOrigin(), 
426                 filler.getJasperPrint().getDefaultStyleProvider(), 
427                 this
428                 );
429         template.copyParagraph(getPrintParagraph());
430         template.copyLineBox(getPrintLineBox());
431         template.setTextFormat(textFormat);
432         return template;
433     }
434
435
436     protected void evaluateTextFormat(Format format, Object value, TimeZone ownTimeZone)
437     {
438         if (value != null)
439 //        if (getExpression() != null)
440         {
441             if (value instanceof String)
442             {
443                 textFormat = null;
444             }
445             else
446             {
447                 SimpleTextFormat simpleTextFormat = new SimpleTextFormat();
448                 
449                 simpleTextFormat.setValueClassName(value.getClass().getName());
450
451                 String pattern = getTemplatePattern(format, value);
452                 if (pattern != null)
453                 {
454                     simpleTextFormat.setPattern(pattern);
455                 }
456                 
457                 if (!filler.hasMasterFormatFactory())
458                 {
459                     simpleTextFormat.setFormatFactoryClass(filler.getFormatFactory().getClass().getName());
460                 }
461                 
462                 if (!filler.hasMasterLocale())
463                 {
464                     simpleTextFormat.setLocaleCode(JRDataUtils.getLocaleCode(filler.getLocale()));
465                 }
466
467                 if (value instanceof java.util.Date)
468                 {
469                     // the element's format timezone property has precedence over the report timezone
470                     TimeZone formatTimeZone = ownTimeZone == null ? filler.getTimeZone() : ownTimeZone;
471                     // check if the current format timezone differs from the master report timezone
472                     if (!formatTimeZone.equals(filler.fillContext.getMasterTimeZone()))
473                     {
474                         simpleTextFormat.setTimeZoneId(JRDataUtils.getTimeZoneId(formatTimeZone));
475                     }
476                 }
477                 
478                 textFormat = simpleTextFormat;
479             }
480         }
481     }
482
483     @Override
484     protected JRTemplateElement getTemplate(JRStyle style)
485     {
486         Pair<JRStyle, TextFormat> key = new Pair<JRStyle, TextFormat>(style, textFormat);
487         return textTemplates.get(key);
488     }
489
490     @Override
491     protected void registerTemplate(JRStyle style, JRTemplateElement template)
492     {
493         Pair<JRStyle, TextFormat> key = new Pair<JRStyle, TextFormat>(style, textFormat);
494         textTemplates.put(key, template);
495         
496         if (log.isDebugEnabled())
497         {
498             log.debug("created " + template + for " + key);
499         }
500     }
501
502     @Override
503     protected boolean delayedEvaluationUpdatesTemplate()
504     {
505         // since the text format is evaluated during the delayed evaluation, 
506         // we need to always attempt to update the template.
507         // we could test whether the value is String, but there might be some exotic
508         // cases in which the same text field is used for both Strings and numbers.
509         return true;
510     }
511
512
513     protected TimeZone toFormatTimeZone(String timezoneId)
514     {
515         JRFillDataset dataset = expressionEvaluator.getFillDataset();
516         // not sure whether the dataset can be null, let's be safe
517         TimeZone reportTimeZone = dataset == null ? filler.getTimeZone() : dataset.timeZone;
518         
519         return JRDataUtils.resolveFormatTimeZone(timezoneId, reportTimeZone);
520     }
521
522
523     @Override
524     public void evaluate(
525         byte evaluation
526         ) throws JRException
527     {
528         initDelayedEvaluations();
529         
530         reset();
531         
532         evaluatePrintWhenExpression(evaluation);
533
534         if (isPrintWhenExpressionNull() || isPrintWhenTrue())
535         {
536             bookmarkLevel = getBookmarkLevel(evaluateExpression(getBookmarkLevelExpression(), evaluation));
537
538             if (isEvaluateNow())
539             {
540                 evaluateText(evaluation);
541             }
542         }
543     }
544
545
546     /**
547      *
548      */

549     protected void evaluateText(
550         byte evaluation
551         ) throws JRException
552     {
553         evaluateProperties(evaluation);
554         
555         value = evaluateExpression(getExpression(), evaluation);
556         determineOwnTimeZone();
557         
558         evaluateStyle(evaluation);
559         
560         String strValue = null;
561
562         pattern = (String) evaluateExpression(getPatternExpression(), evaluation);
563
564         if (value == null)
565         {
566             if (isBlankWhenNull())
567             {
568                 strValue = "";
569             }
570             else
571             {
572                 strValue = null;
573             }
574         }
575         else
576         {
577             Format format = getFormat(value, ownTimeZone);
578
579             evaluateTextFormat(format, value, ownTimeZone);
580
581             if (format == null)
582             {
583                 strValue = value.toString();
584             }
585             else
586             {
587                 strValue = format.format(value);
588                 
589                 if (value instanceof java.util.Date && log.isDebugEnabled())
590                 {
591                     log.debug(getUUID() + ": formatted value " + value 
592                             + " (" + value.getClass().getName() + "/" + ((java.util.Date) value).getTime() + ")"
593                             + " to " + strValue);
594                 }
595             }
596         }
597
598         String crtRawText = getRawText();
599         String newRawText = processMarkupText(String.valueOf(strValue));
600
601         setRawText(newRawText);
602         resetTextChunk();
603
604         setValueRepeating(
605             (crtRawText == null && newRawText == null) ||
606             (crtRawText != null && crtRawText.equals(newRawText))
607             );
608
609         anchorName = (String) evaluateExpression(getAnchorNameExpression(), evaluation);
610         hyperlinkReference = (String) evaluateExpression(getHyperlinkReferenceExpression(), evaluation);
611         hyperlinkWhen = (Boolean) evaluateExpression(getHyperlinkWhenExpression(), evaluation);
612         hyperlinkAnchor = (String) evaluateExpression(getHyperlinkAnchorExpression(), evaluation);
613         hyperlinkPage = (Integer) evaluateExpression(getHyperlinkPageExpression(), evaluation);
614         hyperlinkTooltip = (String) evaluateExpression(getHyperlinkTooltipExpression(), evaluation);
615         hyperlinkParameters = JRFillHyperlinkHelper.evaluateHyperlinkParameters(this, expressionEvaluator, evaluation);
616     }
617
618     @Override
619     protected TimeZone getTimeZone()
620     {
621         return ownTimeZone == null ? super.getTimeZone() : ownTimeZone;
622     }
623
624     protected void determineOwnTimeZone()
625     {
626         ownTimeZone = null;
627         if (value instanceof java.util.Date)
628         {
629             // read the element's format timezone property
630             String ownTimezoneId = hasProperties() ? getPropertiesMap().getProperty(PROPERTY_FORMAT_TIMEZONE) : null;
631             ownTimeZone = toFormatTimeZone(ownTimezoneId);
632             
633             if (ownTimeZone == null)
634             {
635                 // trying to get a timezone for the specific date/time type.
636                 // should we have timezones for arbitrary types a la oracle.sql.DATE?
637                 if (value instanceof java.sql.Date)
638                 {
639                     ownTimeZone = getPatternTimeZone(PROPERTY_SQL_DATE_FORMAT_TIMEZONE);
640                 }
641                 else if (value instanceof java.sql.Timestamp)
642                 {
643                     ownTimeZone = getPatternTimeZone(PROPERTY_SQL_TIMESTAMP_FORMAT_TIMEZONE);
644                 }
645                 else if (value instanceof java.sql.Time)
646                 {
647                     ownTimeZone = getPatternTimeZone(PROPERTY_SQL_TIME_FORMAT_TIMEZONE);
648                 }
649             }
650             
651             if (ownTimeZone == null)
652             {
653                 // using a general timezone
654                 ownTimeZone = getPatternTimeZone(PROPERTY_FORMAT_TIMEZONE);
655             }
656         }
657     }
658
659
660     protected TimeZone getPatternTimeZone(String property)
661     {
662         if (generalPatternTimeZones.containsKey(property))
663         {
664             return generalPatternTimeZones.get(property);
665         }
666         
667         String propertyVal = filler.getPropertiesUtil().getProperty(filler.getMainDataset(), property);
668         TimeZone timeZone = toFormatTimeZone(propertyVal);
669         generalPatternTimeZones.put(property, timeZone);
670         
671         if (log.isDebugEnabled())
672         {
673             log.debug(getUUID() + ": pattern timezone property " + property 
674                     + " is " + propertyVal + ", resolved to " + timeZone);
675         }
676         
677         return timeZone;
678     }
679
680
681     @Override
682     public boolean prepare(
683         int availableHeight,
684         boolean isOverflow
685         ) throws JRException
686     {
687         boolean willOverflow = false;
688
689         super.prepare(availableHeight, isOverflow);
690
691         if (!isToPrint())
692         {
693             return willOverflow;
694         }
695
696         boolean isToPrint = true;
697         boolean isReprinted = false;
698
699         if (isEvaluateNow())
700         {
701             if (isOverflow)
702             {
703                 if (getPositionTypeValue() == PositionTypeEnum.FIX_RELATIVE_TO_BOTTOM)
704                 {
705                     // the content of the band bottom text fields is not
706                     // consumed during overflows, because they only appear on the last overflow
707                     resetTextChunk();
708                 }
709
710                 if (
711                     getTextEnd() >= getTextString().length()
712                     || !isStretchWithOverflow()
713                     || !getRotationValue().equals(RotationEnum.NONE)
714                     )
715                 {
716                     // there is no more text left in the text field to overflow
717                     // on the new page, or the text field is not stretchable
718                     
719                     if (isAlreadyPrinted())
720                     {
721                         // the text field has already displayed all its content
722                         // on the previous page even if it not stretchable
723                         
724                         if (isPrintWhenDetailOverflows())
725                         {
726                             // the whole content is reprinted
727                             resetTextChunk();
728
729                             isReprinted = true;
730                         }
731                         else
732                         {
733                             isToPrint = false;
734                         }
735                     }
736 //                    else
737 //                    {
738 //                        // the text field did not print on the previous page.
739 //                        // we let it go since it is its first time anyway
740 //                    }
741                 }
742 //                else
743 //                {
744 //                    // there is text left inside the stretchable text field.
745 //                    // we simply let it go
746 //                }
747
748                 if (
749                     isToPrint &&
750                     isPrintWhenExpressionNull() &&
751                     !isPrintRepeatedValues() &&
752                     isValueRepeating()
753                     )
754                 {
755                     isToPrint = false// FIXME, shouldn't we test for the first whole band and the other exceptions to the rule?
756                 }
757             }
758             else
759             {
760                 if (
761                     isPrintWhenExpressionNull() &&
762                     !isPrintRepeatedValues() &&
763                     isValueRepeating()
764                     )
765                 {
766                     if (
767                         ( !isPrintInFirstWholeBand() || !getBand().isFirstWholeOnPageColumn() ) &&
768                         ( getPrintWhenGroupChanges() == null || !getBand().isNewGroup(getPrintWhenGroupChanges()) )
769                         )
770                     {
771                         isToPrint = false;
772                     }
773                 }
774             }
775
776             if (isToPrint)
777             {
778                 if (availableHeight >= getRelativeY() + getHeight())
779                 {
780                     // the available vertical space is sufficient
781
782                     if (
783                         getTextEnd() < getTextString().length() 
784                         || getTextEnd() == 0
785                         )
786                     {
787                         // there is still some text left in the text field or
788                         // the text field is empty
789
790                         if (
791                             isStretchWithOverflow()
792                             && getRotationValue().equals(RotationEnum.NONE)
793                             )
794                         {
795                             // the text field is allowed to stretch downwards in order to
796                             // display all its content
797
798                             chopTextElement(availableHeight - getRelativeY() - getHeight());
799                             if (getTextEnd() < getTextString().length())// - 1)
800                             {
801                                 // even after the current chop operation there is some text left
802                                 // that will overflow on the next page
803
804                                 willOverflow = true;
805                             }
806                         }
807                         else
808                         {
809                             // the text field is not allowed to stretch downwards in order to
810                             // display all its content
811
812                             chopTextElement(0);
813                         }
814                     }
815                     else
816                     {
817                         // there is no text left in the text field and the text field was not empty
818
819                         // this section is probably unreachable since it is most likely that
820                         // the isToPrint flag was already set on false in the code above.
821                         isToPrint = false;
822                     }
823                 }
824                 else
825                 {
826                     // the available vertical space is not sufficient
827
828                     // no matter if there is some text left inside or not,
829                     // there was an explicit request to display it, 
830                     // even if we are on an overflow.
831                     // since there is no space available, it will overflow
832                     
833                     isToPrint = false;
834                     willOverflow = true;
835                 }
836             }
837
838             if (
839                 isToPrint &&
840                 isRemoveLineWhenBlank() &&    //FIXME if the line won't be removed due to other elements 
841                 getTextString().substring(        // present on that line, the background does not appear
842                     getTextStart(),
843                     getTextEnd()
844                     ).trim().length() == 0
845                 )
846             {
847                 isToPrint = false;
848             }
849         }
850         else
851         {
852             if (isOverflow && isAlreadyPrinted())
853             {
854                 if (isPrintWhenDetailOverflows())
855                 {
856                     isReprinted = true;
857                 }
858                 else
859                 {
860                     isToPrint = false;
861                 }
862             }
863             
864             if (
865                 isToPrint && 
866                 availableHeight < getRelativeY() + getHeight()
867                 )
868             {
869                 isToPrint = false;
870                 willOverflow = true;
871             }
872         }
873
874         setToPrint(isToPrint);
875         setReprinted(isReprinted);
876
877         return willOverflow;
878     }
879
880
881     @Override
882     public JRPrintElement fill() throws JRException
883     {
884         EvaluationTimeEnum evaluationTime = getEvaluationTimeValue();
885         
886         JRTemplatePrintText text;
887         JRRecordedValuesPrintText recordedValuesText;
888         if (isEvaluateAuto())
889         {
890             text = recordedValuesText = new JRRecordedValuesPrintText(getJRTemplateText(), printElementOriginator);
891         }
892         else
893         {
894             text = new JRTemplatePrintText(getJRTemplateText(), printElementOriginator);
895             recordedValuesText = null;
896         }
897         
898         text.setUUID(getUUID());
899         text.setX(getX());
900         text.setY(getRelativeY());
901         text.setWidth(getWidth());
902 //        if (getRotation() == ROTATION_NONE)
903 //        {
904             //text.setHeight(getPrintElementHeight());
905             text.setHeight(getStretchHeight());
906 //        }
907 //        else
908 //        {
909 //            text.setHeight(getHeight());
910 //        }
911         text.setRunDirection(getRunDirectionValue());
912         text.setBookmarkLevel(getBookmarkLevel());
913
914         if (isEvaluateNow())
915         {
916             copy(text);
917         }
918         else if (isEvaluateAuto())
919         {
920             initDelayedEvaluationPrint(recordedValuesText);
921         }
922         else
923         {
924             filler.addBoundElement(this, text, evaluationTime, getEvaluationGroup(), band);
925         }
926
927         return text;
928     }
929
930
931     /**
932      *
933      */

934     protected void copy(JRPrintText text)
935     {
936         text.setLineSpacingFactor(getLineSpacingFactor());
937         text.setLeadingOffset(getLeadingOffset());
938         text.setTextHeight(getTextHeight());
939         //FIXME rotation and run direction?
940
941         //FIXME do we need to do this when the value is String?
942         text.setValue(getValue());
943         
944         setPrintText(text);
945
946         text.setAnchorName(getAnchorName());
947         if (getHyperlinkWhenExpression() == null || Boolean.TRUE.equals(hyperlinkWhen))
948         {
949             text.setHyperlinkReference(getHyperlinkReference());
950             text.setHyperlinkAnchor(getHyperlinkAnchor());
951             text.setHyperlinkPage(getHyperlinkPage());
952             text.setHyperlinkTooltip(getHyperlinkTooltip());
953             text.setHyperlinkParameters(hyperlinkParameters);
954         }
955         else
956         {
957             if (text instanceof JRTemplatePrintText)//this is normally the case
958             {
959                 ((JRTemplatePrintText) text).setHyperlinkOmitted(true);
960             }
961             
962             text.setHyperlinkReference(null);
963         }
964         transferProperties(text);
965     }
966
967     @Override
968     protected void setPrintText(JRPrintText printText, String text)
969     {
970         // checking if the text is identical to the one set via setValue.
971         // note that we're assuming that this method is called after printText.setValue().
972         // JRStyledText no longer creates String copies for simple texts, but keeping this to cover other cases.
973         Object printValue = printText.getValue();
974         String textObj = text;
975         if (text != null && printValue != null && printValue instanceof String
976                 && text.equals(printValue))
977         {
978             textObj = (String) printValue;
979         }
980         super.setPrintText(printText, textObj);
981     }
982     
983     /**
984      *
985      */

986     protected Format getFormat(Object value, TimeZone ownTimeZone)//FIXMEFORMAT optimize this with an interface
987     {
988         Format format = null;
989
990         if (value instanceof java.util.Date)
991         {
992             format = filler.getDateFormat(getDatePattern(value), ownTimeZone);
993         }
994         else if (value instanceof java.lang.Number)
995         {
996             format = filler.getNumberFormat(getNumberPattern(value));
997         }
998         
999         return format;
1000     }
1001
1002     /**
1003      *
1004      */

1005     protected String getTemplatePattern(Format format, Object value)//FIXMEFORMAT optimize this with an interface
1006     {
1007         String pattern = null;
1008         String originalPattern;
1009
1010         if (value instanceof java.util.Date)
1011         {
1012             originalPattern = getDatePattern(value);
1013             if (format instanceof SimpleDateFormat)
1014             {
1015                 pattern = ((SimpleDateFormat) format).toPattern();
1016             }
1017         }
1018         else if (value instanceof Number)
1019         {
1020             originalPattern = getNumberPattern(value);
1021             if (format instanceof DecimalFormat)
1022             {
1023                 pattern = ((DecimalFormat) format).toPattern();
1024             }
1025         }
1026         else
1027         {
1028             originalPattern = getPattern();
1029         }
1030         
1031         if (pattern == null)//fallback to the original pattern
1032         {
1033             pattern = originalPattern;
1034         }
1035         
1036         return pattern;        
1037     }
1038     
1039     
1040     @Override
1041     public void collectExpressions(JRExpressionCollector collector)
1042     {
1043         collector.collect(this);
1044     }
1045
1046     @Override
1047     public void visit(JRVisitor visitor)
1048     {
1049         visitor.visitTextField(this);
1050     }
1051     
1052
1053     @Override
1054     protected void resolveElement(JRPrintElement element, byte evaluation) throws JRException
1055     {
1056         evaluateText(evaluation);
1057
1058         chopTextElement(0);
1059
1060         copy((JRPrintText) element);
1061         
1062         //FIXME put this somewhere else, e.g. in ElementEvaluationAction
1063         filler.updateBookmark(element);
1064     }
1065
1066
1067     @Override
1068     public int getBookmarkLevel()
1069     {
1070         return bookmarkLevel == null ? ((JRTextField) parent).getBookmarkLevel() : bookmarkLevel;
1071     }
1072
1073
1074     @Override
1075     public JRFillCloneable createClone(JRFillCloneFactory factory)
1076     {
1077         return new JRFillTextField(this, factory);
1078     }
1079     
1080     @Override
1081     protected void collectDelayedEvaluations()
1082     {
1083         super.collectDelayedEvaluations();
1084         
1085         collectDelayedEvaluations(getExpression());
1086         collectDelayedEvaluations(getPatternExpression());
1087         collectDelayedEvaluations(getAnchorNameExpression());
1088         collectDelayedEvaluations(getHyperlinkReferenceExpression());
1089         collectDelayedEvaluations(getHyperlinkWhenExpression());
1090         collectDelayedEvaluations(getHyperlinkAnchorExpression());
1091         collectDelayedEvaluations(getHyperlinkPageExpression());    
1092     }
1093
1094
1095     @Override
1096     public JRHyperlinkParameter[] getHyperlinkParameters()
1097     {
1098         return ((JRTextField) parent).getHyperlinkParameters();
1099     }
1100
1101
1102     @Override
1103     public String getLinkType()
1104     {
1105         return ((JRTextField) parent).getLinkType();
1106     }
1107
1108
1109     @Override
1110     public JRExpression getHyperlinkTooltipExpression()
1111     {
1112         return ((JRTextField) parent).getHyperlinkTooltipExpression();
1113     }
1114
1115
1116     @Override
1117     protected boolean canOverflow()
1118     {
1119         return 
1120             getTextAdjust() == TextAdjustEnum.STRETCH_HEIGHT
1121             && getRotationValue() == RotationEnum.NONE
1122             && isEvaluateNow()
1123             && filler.isBandOverFlowAllowed();
1124     }
1125
1126
1127     @Override
1128     protected boolean scaleFontToFit()
1129     {
1130         return getTextAdjust() == TextAdjustEnum.SCALE_FONT;
1131     }
1132     
1133 }
1134